How do I test for the equality of two unordered lists?

If you know that there can never be duplicates, you can use a set (HashSet or BTreeSet, depending on your types):

use std::{collections::HashSet, hash::Hash};

fn my_eq<T>(a: &[T], b: &[T]) -> bool
where
    T: Eq + Hash,
{
    let a: HashSet<_> = a.iter().collect();
    let b: HashSet<_> = b.iter().collect();

    a == b
}

fn main() {
    assert!(my_eq(
        &["foo", "bar", "baz", "beh"],
        &["beh", "foo", "baz", "bar"]
    ));
    assert!(!my_eq(
        &["beh", "foo", "baz", "bar"],
        &["beh", "foo", "baz", "baz"]
    ));
}

If you need to handle duplicates, you'll want to count the number of values too:

use std::{collections::HashMap, hash::Hash};

fn my_eq<T>(a: &[T], b: &[T]) -> bool
where
    T: Eq + Hash,
{
    fn count<T>(items: &[T]) -> HashMap<&T, usize>
    where
        T: Eq + Hash,
    {
        let mut cnt = HashMap::new();
        for i in items {
            *cnt.entry(i).or_insert(0) += 1
        }
        cnt
    }

    count(a) == count(b)
}

fn main() {
    assert!(my_eq(
        &["foo", "foo", "baz", "beh"],
        &["beh", "foo", "baz", "foo"]
    ));
    assert!(!my_eq(
        &["foo", "foo", "baz", "beh"],
        &["beh", "foo", "baz"]
    ));
}

If you want to be super fancy, you can create a newtype that adds this type of equality directly:

use std::{collections::HashMap, hash::Hash};

#[derive(Debug, Copy, Clone)]
struct CustomEq<'a, T: 'a>(&'a [T]);

impl<'a, T> CustomEq<'a, T>
where
    T: Eq + Hash,
{
    fn count(&self) -> HashMap<&T, usize> {
        let mut cnt = HashMap::new();
        for i in self.0 {
            *cnt.entry(i).or_insert(0) += 1
        }
        cnt
    }
}

impl<'a, T> PartialEq for CustomEq<'a, T>
where
    T: Eq + Hash,
{
    fn eq(&self, other: &Self) -> bool {
        self.count() == other.count()
    }
}

fn main() {
    assert_eq!(
        CustomEq(&["foo", "bar", "baz", "beh"]),
        CustomEq(&["beh", "foo", "baz", "bar"])
    );
    assert_ne!(
        CustomEq(&["beh", "foo", "baz", "bar"]),
        CustomEq(&["beh", "foo", "baz", "baz"])
    );
}

Tags:

List

Rust