abc
abc

Reputation: 69

How do I get the cartesian product of 2 vectors by using Iterator?

I have 2 Vecs:

let x = vec!['1', '2', '3'];
let y = vec!['a', 'b', 'c'];

Now I want to use iterator to make a new vec like this ['1a', '1b', '1c', '2a', '2b', '2c', '3a', '3b', '3c']. How can I do?

Upvotes: 4

Views: 8244

Answers (3)

Julian
Julian

Reputation: 2654

The existing answers make sense if your goal is to get the Cartesian product of two iterators. If you've got vectors or slices already though (like in the original question) you can do a little better:

fn main() {
    let x = vec!['1', '2', '3'];
    let y = vec!['a', 'b', 'c'];
    let result: Vec<String> = product(&x, &y)
        .map(|(a, b)| format!("{}{}", a, b))
        .collect();
    println!("{:?}", result)
}

fn product<'a: 'c, 'b: 'c, 'c, T>(
    xs: &'a [T],
    ys: &'b [T],
) -> impl Iterator<Item = (&'a T, &'b T)> + 'c {
    xs.iter().flat_map(move |x| std::iter::repeat(x).zip(ys))
}

Playground

Any iterator based solution will necessarily require storing the full contents of the iterator somewhere, but if you already have the data in a vector or array you can use the known size to only store the indices instead.

Upvotes: 6

Maxim Gritsenko
Maxim Gritsenko

Reputation: 2592

Here is how to do it with vanilla Rust iterators:

fn main() {
    let x = vec!['1', '2', '3'];
    let y = vec!['a', 'b', 'c'];
    let product: Vec<String> = x
        .iter()
        .map(|&item_x| y
            .iter()
            .map(move |&item_y| [item_x, item_y]
                .iter()
                .collect()
                )
            )
        .flatten()
        .collect();
    
    println!("{:?}", product);
}

Explanation

The easiest way to construct a String from two chars is to collect iterator over the chars:

let string: String = [item_x, item_y].iter().collect();

For each item in x we iterate over y and construct such string.

x.iter().map(|&item_x| y.iter.map(move |&item_y| ...));

We use pattern matching to get value in the map closure rather then references. Because of that and the fact that the char has Copy trait, we can move item_x into inner closure, resolving any lifetime issues.

As the result of the code above we get an iterator over iterators over Strings. To flatten that iterator, we use flatten method (who would think?). Then we collect the flat iterator into the resulting Vec.

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bf2987ed96303a0db0f629884492011e

Upvotes: 7

Netwave
Netwave

Reputation: 42796

Easiest way would be to use the cartesian product macro available in the itertools crate

use itertools::iproduct; // 0.10.1

fn main() {
    let x = vec!['1', '2', '3'];
    let y = vec!['a', 'b', 'c'];
    let product: Vec<String> = iproduct!(x, y)
        .map(|(a, b)| format!("{}{}", a, b))
        .collect();
    println!("{:?}", product);
}

Playground

Upvotes: 14

Related Questions