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_byoudedup_by_keyserait 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::IndexSetpour garder l'ordre, et éventuellement revenir à un vecteur.