17

What's the best way to compare 2 vectors or strings element by element in Rust, while being able to do processing on each pair of elements? For example if you wanted to keep count of the number of differing elements. This is what I'm using:

let mut diff_count: i32 = 0i32;
for (x, y) in a.chars().zip(b.chars()) {
    if x != y {
        diff_count += 1i32;
    }
}

Is that the correct way or is there something more canonical?

anderspitman
  • 9,230
  • 10
  • 40
  • 61
  • FYI, there's no need to specify the type (`count: i32`) **and** use a type suffix (`0i32`). Once you pick one of those, you don't need to specify the type when adding another literal (`1i32`). – Shepmaster Apr 08 '15 at 02:09

2 Answers2

31

To get the count of matching elements, I'd probably use filter and count.

fn main() {
    let a = "Hello";
    let b = "World";

    let matching = a.chars().zip(b.chars()).filter(|&(a, b)| a == b).count();
    println!("{}", matching);

    let a = [1, 2, 3, 4, 5];
    let b = [1, 1, 3, 3, 5];

    let matching = a.iter().zip(&b).filter(|&(a, b)| a == b).count();
    println!("{}", matching);
}
  • Iterator::zip takes two iterators and produces another iterator of the tuple of each iterator's values.
  • Iterator::filter takes a reference to the iterator's value and discards any value where the predicate closure returns false. This performs the comparison.
  • Iterator::count counts the number of elements in the iterator.

Note that Iterator::zip stops iterating when one iterator is exhausted. If you need different behavior, you may also be interested in Itertools::zip_longest or Itertools::zip_eq.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
7

If you wanted to use @Shepmaster's answer as the basis of an assertion to be used in a unit test, try this:

fn do_vecs_match<T: PartialEq>(a: &Vec<T>, b: &Vec<T>) -> bool {
    let matching = a.iter().zip(b.iter()).filter(|&(a, b)| a == b).count();
    matching == a.len() && matching == b.len()
}

Of course, be careful when using this on floats! Those pesky NaNs won't compare, and you might want to use a tolerance for comparing the other values. And you might want to make it fancy by telling the index of the first nonmatching value.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Paul Chernoch
  • 5,275
  • 3
  • 52
  • 73
  • [Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?](https://stackoverflow.com/q/40006219/155423). `zip` takes `IntoIterator`, so you can just do `.zip(b)`. You could micro-optimize by checking to see if the two slices are the same length before doing any iteration. – Shepmaster Nov 15 '19 at 14:44