Jeffrey04
Jeffrey04

Reputation: 6338

Is there a way to remove entries from a generic first vector that are present in another vector?

I have a problem understanding ownership when a higher order function is called. I am supposed to remove entries from the first vector if the elements exist in the second vector so I came up with this attempt:

fn array_diff<T: PartialEq>(a: Vec<T>, b: Vec<T>) -> Vec<T> {
    a.iter()
        .filter(|incoming| !b.contains(incoming))
        .collect::<Vec<T>>()
}

I can't change the function signature. The .collect() call doesn't work because all I am getting is a reference to elements in a. While this is generic, I don't know if the result is copy-able or clone-able. I also probably can't dereference the elements in a.

Is there a way to fix this piece of code without rewriting it from scratch?

Upvotes: 0

Views: 200

Answers (2)

Shepmaster
Shepmaster

Reputation: 430673

Destroying the incoming allocation to create a new allocation isn't very efficient. Instead, write code that is more directly in line with the problem statement:

fn array_diff<T: PartialEq>(mut a: Vec<T>, b: Vec<T>) -> Vec<T> {
    a.retain(|aa| !b.contains(aa));
    a
}

Adding mut in the signature doesn't change the signature because no one can tell that you've added it. It's the exact same as:

fn array_diff<T: PartialEq>(a: Vec<T>, b: Vec<T>) -> Vec<T> {
    let mut a = a;
    a.retain(|aa| !b.contains(aa));
    a
}

Upvotes: 1

Simon Whitehead
Simon Whitehead

Reputation: 65079

For this particular test ... you can consume the vector instead of relying on references. The signature yields values and not references. As such, to pass the test you only have to use into_iter instead:

a.into_iter() // <----------- call into_iter
    .filter(|incoming| !b.contains(incoming))
    .collect::<Vec<T>>()

This consumes the values and returns them out again.

Upvotes: 2

Related Questions