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"]));
}
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])
}
}
.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 anOption
containing a reference to the first element if the previous.all()
returnstrue
, otherwise it returnsNone
- The
match items.is_empty()
is required because.all()
will also returntrue
if the slice is empty, which will result in a panic atitems[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.