Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Whats the best way to join many vectors into a new vector?

Tags:

vector

rust

To create a new vector with the contents of other vectors, I'm currently doing this:

fn func(a: &Vec<i32>, b: &Vec<i32>, c: &Vec<i32>) {
    let abc = Vec<i32> = {
        let mut tmp = Vec::with_capacity(a.len(), b.len(), c.len());
        tmp.extend(a);
        tmp.extend(b);
        tmp.extend(c);
        tmp
    };

    // ...
}

Is there a more straightforward / elegant way to do this?

like image 574
ideasman42 Avatar asked Sep 01 '16 05:09

ideasman42


People also ask

How do we combine vectors?

We can combine vectors by adding them, the sum of two vectors is called the resultant. In order to add two vectors, we add the corresponding components.

How do I append multiple vectors in R?

To concatenate a vector or multiple vectors in R use c() function. This c() function is used to combine the objects by taking two or multiple vectors as input and returning the vector with the combined elements from all vectors.

Can we add 2 vectors directly?

No, vector addition does not apply to any two vectors. Two vectors are added only when they are of the same type and nature. For instance, two velocity vectors can be added, but one velocity vector and one force vector cannot be added.

What is a vector that combines two vectors?

This is called a linear combination. We call the set of all the vectors that are reachable by our set of vectors the span of those vectors.


2 Answers

There is a concat method that can be used for this, however the values need to be slices, or borrowable to slices, not &Vec<_> as given in the question.

An example, similar to the question:

fn func(a: &Vec<i32>, b: &Vec<i32>, c: &Vec<i32>) {
    let abc = Vec<i32> = [a.as_slice(), b.as_slice(), c.as_slice()].concat();

    // ...
}

However, as @mindTree notes, using &[i32] type arguments is more idiomatic and removes the need for conversion. eg:

fn func(a: &[i32], b: &[i32], c: &[i32]) {
    let abc = Vec<i32> = [a, b, c].concat();

    // ...
}

SliceConcatExt::concat is a more general version of your function and can join multiple slices to a Vec. It will sum the sizes each slice to pre-allocate a Vec of the right capacity, then extend repeatedly.

fn concat(&self) -> Vec<T> {
    let size = self.iter().fold(0, |acc, v| acc + v.borrow().len());
    let mut result = Vec::with_capacity(size);
    for v in self {
        result.extend_from_slice(v.borrow())
    }
    result
}
like image 78
tilpner Avatar answered Nov 03 '22 21:11

tilpner


One possible solution might be to use the Chain iterator:

let abc: Vec<_> = a.iter().chain(b).chain(c).collect();

However, in your example you are borrowing the slices, so we'll need to either deref each borrowed element or use the Cloned iterator to copy each integer. Cloned is probably a bit easier and as efficient as we are working with small Copy data (i32):

let abc: Vec<_> = a.iter().cloned()
    .chain(b.iter().cloned())
    .chain(c.iter().cloned())
    .collect();

Seeing as each of these iterators are ExactSizeIterators, it should be possible to allocate the exact size for the target Vec up front, however I'm unware whether or not this is actually the case in the std implementation (they might be waiting on specialization to land before adding this optimisation).

like image 3
mindTree Avatar answered Nov 03 '22 20:11

mindTree