The best way to write set operations

I end up having to write something like

        // Calculate the union of three sets that are intersections of three original sets
        let i1: BTreeSet<Item> = set1.intersection(&set2).map(|v| *v).collect();
        let i2: BTreeSet<Item> = set1.intersection(&set3).map(|v| *v).collect();
        let i3: BTreeSet<Item> = set2.intersection(&set3).map(|v| *v).collect();
        let u: Vec<Item> = i1
            .union(&i2)
            .map(|v| *v)
            .collect::<BTreeSet<Item>>()
            .union(&i3)
            .map(|v| *v)
            .collect();

I am not happy that I am not able to write

    let u = set1.intersection(set2).union(set1.intersection(set3)).union(set2.intersection(set3));

or more step by step,

    let i1 = set1.intersection(set2);
    let i2 = set1.intersection(set3);
    let i3 = set2.intersection(set3);
    let u = i1.union(i2).union(i3);

Any idea to write some better workable code for this simple algorithm?

You can, you just need to use operators.

let u = &(&(&set1 & &set2) | &(&set1 & &set3)) | &(&set2 & &set3);

Admitted, the fact that every operand needs to be borrowed makes this a bit less readable than one might like.

@steffahn

That helped a lot! I tried the "step by step" way:

let i1 = &set1 & &set2;
let i2 = &set1 & &set3;
let i3 = &set2 & &set3;
let u = &(&i1 | &i2) | &i3;

I don't think you need to collect in each step.

    let i1 = set1.intersection(&set2);
    let i2 = set1.intersection(&set3);
    let i3 = set2.intersection(&set3);
    let u = i1.chain(i2).chain(i3).copied().collect::<BTreeSet<Item>>();

Yes that's another way. But then it does not use the standard union method, makes the purpose of the code harder to understand.

Talking about efficiency, this can be done in a single iteration e.g.

use itertools::EitherOrBoth::{Both, Left};
use itertools::Itertools;
let u: BTreeSet<Item> = set1
    .iter()
    .merge_join_by(&set2, Ord::cmp)
    .merge_join_by(&set3, |a_b, c| a_b.as_ref().reduce(|a, _b| a).cmp(c))
    .filter_map(|a_b_c| match a_b_c {
        Both(a_b, _c) => Some(a_b.reduce(|a, _b| a)),
        Left(Both(a, _b)) => Some(a),
        _ => None,
    })
    .copied()
    .collect();

(disclaimer: code compiles but is completely untested)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.