Is there a simple way remove duplicate elements from an array?

Viewed 4729

I want to remove duplicate elements from an array:

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},
        ]
    */
}

Way 2 is simple compared to way 1, but is there anything simpler?

3 Answers

There's a difference between the dedup and unique method in Itertools, where the former operates on contiguous elements, i.e:

[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]

If you're looking to have unique elements by name, unique_by might do:

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<_>>();
}
[
  Person { name: "aaa", age: 10 },
  Person { name: "bbb", age: 20 },
  Person { name: "ccc", age: 30 }
]

With nightly Rust, this can be done in-place and with no additional memory allocation 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);
}

This doesn't actually remove the duplicate elements, it just moves them to the end of the original slice / array / Vec. It's impossible to remove values from an array because an array has a fixed length.

See also:

Related