Je souhaite supprimer les éléments en double d'un tableau:
use itertools::Itertools; use std::collections::HashSet; #[derive(Debug)] struct Person { name: String, age: u32, } fn main() { let arr = [ Person { name: "aaa".to_string(), age: 10 }, Person { name: "bbb".to_string(), age: 20 }, Person { name: "bbb".to_string(), age: 20 }, Person { name: "ccc".to_string(), age: 30 }, ]; // Way 1: let arr2 = { let names: Vec<_> = arr.iter().map(|v| v.name.clone()).unique().collect(); names .iter() .map(|name| arr.iter().find(|person| &person.name == name).unwrap()) .collect::<Vec<_>>() }; dbg!(arr2); // Way 2: let arr2 = { let mut names = HashSet::new(); arr.iter() .filter(|p| names.insert(p.name.clone())) .collect::<Vec<_>>() }; dbg!(arr2); /* expect: [ Person{name: "aaa".to_string(), age: 10}, Person{name: "bbb".to_string(), age: 20}, Person{name: "ccc".to_string(), age: 30}, ] */ }
La voie 2 est simple par rapport à la voie 1, mais y a-t-il quelque chose de plus simple?
3 Réponses :
Peut-être que Itertools::unique
et Itertools::unique_by
help. Ils utilisent une approche basée sur le Hash
.
Il y a une différence entre la méthode dedup
et unique
dans Itertools
, où la première opère sur des éléments contigus, c'est-à-dire:
[ Person { name: "aaa", age: 10 }, Person { name: "bbb", age: 20 }, Person { name: "ccc", age: 30 } ]
Si vous cherchez à avoir des éléments uniques par name
, unique_by
pourrait faire:
use itertools::Itertools; #[derive(Debug)] struct Person { name: String, age: u32, } fn main() { let arr = [ Person { name: "aaa".to_string(), age: 10 }, Person { name: "bbb".to_string(), age: 20 }, Person { name: "bbb".to_string(), age: 20 }, // Duplicate Person { name: "ccc".to_string(), age: 30 }, ]; let res = arr.iter().unique_by(|p| &p.name).collect::<Vec<_>>(); }
[1, 2, 2, 3, 4, 3, 2, 1].iter().dedup() // [1, 2, 3, 4, 3, 2, 1] [1, 2, 2, 3, 4, 3, 2, 1].iter().unique() // [1, 2, 3, 4]
Il convient également de noter que l'approche unique
et op repose sur le clonage de quelque chose, alors que unique_by
peut fonctionner sur des références. Honnêtement, je ne sais pas pourquoi unique repose sur le clonage de l'ensemble alors qu'il pourrait tout aussi facilement se fier à une simple référence.
Avec Nightly Rust, cela peut être fait sur place et sans allocation de mémoire supplémentaire via slice::partition_dedup_by
:
#![feature(slice_partition_dedup)] #[derive(Debug)] struct Person { name: String, age: u32, } fn main() { let mut arr = [ Person { name: "aaa".to_string(), age: 10 }, Person { name: "bbb".to_string(), age: 20 }, Person { name: "bbb".to_string(), age: 20 }, Person { name: "ccc".to_string(), age: 30 }, ]; arr.sort_by(|a, b| a.name.cmp(&b.name)); let (unique, _) = arr.partition_dedup_by(|a, b| a.name == b.name); dbg!(unique); }
Cela ne supprime pas réellement les éléments en double, cela les déplace simplement à la fin de la tranche / tableau / Vec
. Il est impossible de supprimer des valeurs d'un tableau car un tableau a une longueur fixe.
Voir également:
Jetez un œil à vec: dedup () Si vous voulez simplement supprimer les éléments contigus dupliqués. Si la commande ne vous dérange pas, vec.sort_by (), puis vec: dedup ()
@ Iñigo
dedup_by
oudedup_by_key
serait plus applicable car ils souhaitent effectuer une déduplication en fonction du nom, et non de la structure Person.Une autre option serait d'utiliser un wrapper implémentant Hash et Eq délégué à
name
(l'implémenter directement sur Person semble être une idée terrible), puis obtenir le tout viaHashSet
, ouIndexMap::IndexSet
pour garder l'ordre, et éventuellement revenir à un vecteur.