Fix unique filter implementation

This commit is contained in:
Guillaume Gomez 2025-04-22 23:04:39 +02:00 committed by René Kijewski
parent 7fccbdf1d7
commit a5b43c0aa2

View File

@ -1,3 +1,4 @@
use core::convert::Infallible;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::Hash;
@ -9,8 +10,7 @@ use std::rc::Rc;
/// the data is wrapped inside `Rc`.
///
/// ```
/// # #[cfg(feature = "code-in-doc")] {
/// # use askama::{Template, filters};
/// # use askama::Template;
/// #[derive(Template)]
/// #[template(ext = "html", source = "{% for elem in example|unique %}{{ elem }},{% endfor %}")]
/// struct Example<'a> {
@ -19,14 +19,15 @@ use std::rc::Rc;
///
/// assert_eq!(
/// Example { example: vec!["a", "b", "a", "c"] }.to_string(),
/// "a,b,c"
/// "a,b,c,"
/// );
/// # }
/// ```
pub fn unique<T: Hash + Eq>(it: impl IntoIterator<Item = T>) -> impl Iterator<Item = Rc<T>> {
pub fn unique<T: Hash + Eq>(
it: impl IntoIterator<Item = T>,
) -> Result<impl Iterator<Item = Rc<T>>, Infallible> {
let mut set = HashMap::new();
it.into_iter().filter_map(move |elem| {
Ok(it.into_iter().filter_map(move |elem| {
// To prevent cloning the data, we need to use `Rc`, like that we can clone `elem` as
// key of the `HashSet` and return it.
if let Entry::Vacant(entry) = set.entry(Rc::new(elem)) {
@ -34,7 +35,7 @@ pub fn unique<T: Hash + Eq>(it: impl IntoIterator<Item = T>) -> impl Iterator<It
} else {
None
}
})
}))
}
#[cfg(test)]
@ -47,15 +48,15 @@ mod test {
#[test]
fn test_unique() {
assert_eq!(
unique(["a", "b", "a", "c"]).collect::<Vec<_>>(),
unique(["a", "b", "a", "c"]).unwrap().collect::<Vec<_>>(),
vec![Rc::new("a"), Rc::new("b"), Rc::new("c")]
);
assert_eq!(
unique([1, 1, 1, 2, 1]).collect::<Vec<_>>(),
unique([1, 1, 1, 2, 1]).unwrap().collect::<Vec<_>>(),
vec![Rc::new(1), Rc::new(2)]
);
assert_eq!(
unique("hello".chars()).collect::<Vec<_>>(),
unique("hello".chars()).unwrap().collect::<Vec<_>>(),
vec![Rc::new('h'), Rc::new('e'), Rc::new('l'), Rc::new('o')]
);
}