Reputation: 8048
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> {
// ... ?
}
Upvotes: 7
Views: 4786
Reputation: 602315
I think this is a nice use case for subslice patterns:
pub fn are_all_elements_equal<T: PartialEq>(elems: &[T]) -> Option<&T> {
match elems {
[head, tail @ ..] => tail.iter().all(|x| x == head).then(|| head),
[] => None,
}
}
Upvotes: 11
Reputation: 2197
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"]));
}
Upvotes: 4
Reputation: 5613
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 an Option
containing a reference to the first element if the previous .all()
returns true
, otherwise it returns None
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.
Upvotes: 1
Reputation: 155286
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()
.
Upvotes: 0
Reputation: 9647
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)
Upvotes: 6