Home > Back-end >  How to know if all slice elements are equal, and if so, return a reference to the first one?
How to know if all slice elements are equal, and if so, return a reference to the first one?

Time:10-01

Given any slice, for example:

let words = &["one", "one", "one", "two"];

How to know if all elements are the same?

Even further, if all elements are the same, how to return a reference to the first one?

Essentially, I'm trying to write a function like:

fn are_all_elements_equal<T>(elems: &[T]) -> Option<&T> {
    // ... ?
}

CodePudding user response:

I'd use .all: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.all

First, if the slice is empty, just return None.

Then grab yourself an iterator over the rest of the slice, and use the .all function to check that the element equals the first element that you just grabbed. If that returns true, return your Some(first_element)

CodePudding user response:

As an extension to the answers already posted, you can also make it generic over anything that can be iterated:

pub fn iter_all_eq<T: PartialEq>(iter: impl IntoIterator<Item = T>) -> Option<T> {
    let mut iter = iter.into_iter();
    let first = iter.next()?;
    iter.all(|elem| elem == first).then(|| first)
}

fn main() {
    println!("{:?}", iter_all_eq(&[1, 1, 1]));
    println!("{:?}", iter_all_eq(&[1, 2, 1]));
    println!("{:?}", iter_all_eq(&["abc", "abc", "abc", "abc"]));
}

Playground

CodePudding user response:

An elegant way to do this is using tuple_windows from the itertools crate:

use itertools::Itertools;

pub fn are_all_elements_equal<T: Eq>(elems: &[T]) -> Option<&T> {
    elems.iter().tuple_windows().all(|(a, b)| a == b).then(|| &elems[0])
}

Note that this will panic on an empty slice. To handle the empty slice, you need to explicitly return None if elems.is_empty().

CodePudding user response:

It's fairly simple to do using the available built-in functions:

fn check_all<T: Eq>(items: &[T]) -> Option<&T> {
    match items.is_empty() {
        true => None,
        false => items.windows(2).all(|a| a[0] == a[1]).then(|| &items[0])
    }
}

Playground link

  • .windows(2) gives you an iterator containing overlapping pairs of elements.
  • .all(|a| a[0] == a[1]) compares the two elements from each window
  • .then(|| &items[0]) returns an Option containing a reference to the first element if the previous .all() returns true, otherwise it returns None
  • The match items.is_empty() is required because .all() will also return true if the slice is empty, which will result in a panic at items[0]

Note that since the comparison used in .all() could result in the same value being compared to itself, you need to constrain T to Eq, per this answer.

  • Related