#vec #hash-set #unique-identifier #hash-table #set #identifiable #orderset

identified_vec

Like HashSet but retaining INSERTION order and without Hash requirement on the Element type

8 releases

0.1.11 Dec 22, 2023
0.1.10 Dec 21, 2023

#180 in Data structures

Download history 9/week @ 2023-12-22 15/week @ 2024-01-05 19/week @ 2024-01-12 31/week @ 2024-01-19 12/week @ 2024-01-26 156/week @ 2024-02-09 711/week @ 2024-02-16 407/week @ 2024-02-23 371/week @ 2024-03-01 486/week @ 2024-03-08 346/week @ 2024-03-15 338/week @ 2024-03-22 347/week @ 2024-03-29 386/week @ 2024-04-05

1,472 downloads per month
Used in identified_vec_macros

MIT license

82KB
982 lines

identified_vec

Code Coverage Crates.io Documentation Rust

A collection of unique identifiable elements which retains insertion order, inspired by Pointfree's Swift Identified Collections.

Similar to the standard Vec, the IdentifiedVec maintain their elements in a particular user-specified order. However, unlike Vec, the IdentifiedVec introduce the ability to uniquely identify elements, using a hash table to ensure that no two elements have the same identity, and to efficiently look up elements corresponding to specific identifiers.

IdentifiedVec is a useful alternative to Vec when you need to be able to efficiently access unique elements by a stable identifier. It is also a useful alternative to BTreeSet, where the Ord trait requirement may be too strict, an a useful alternative to HashSet where Hash trait requirement may be too strict.

You can create an identified vec with any element type that implements the Identifiable trait.

Example

extern crate identified_vec;
use identified_vec::{IsIdentifiedVec, IdentifiedVec, Identifiable, IdentifiedVecOf};
use std::cell::RefCell;

#[derive(Eq, PartialEq, Clone, Debug)]
struct User {
    id: &'static str,
    name: RefCell<&'static str>,
}

impl User {
    fn new(id: &'static str, name: &'static str) -> Self {
        Self {
            id,
            name: RefCell::new(name),
        }
    }
    fn name(&self) -> &'static str {
        *self.name.borrow()
    }
}

Identifiable

impl Identifiable for User {
    type ID = &'static str;
    fn id(&self) -> Self::ID {
        self.id
    }
}

from_iter

let mut users = IdentifiedVecOf::<User>::from_iter([
    User::new("u_42", "Satoshi Nakamoto"),
    User::new("u_1337", "Leia Skywalker"),
]);

assert_eq!(
    users.get(&"u_42").map(|u| u.name()),
    Some("Satoshi Nakamoto")
);

assert_eq!(
    users.get_at_index(1).map(|u| u.name()),
    Some("Leia Skywalker")
);

append & elements()

users.append(User::new("u_237", "Alan Turing"));
assert_eq!(
    users.elements(),
    [
        User::new("u_42", "Satoshi Nakamoto"),
        User::new("u_1337", "Leia Skywalker"),
        User::new("u_237", "Alan Turing"),
    ]
    .iter()
    .collect::<Vec<&User>>()
);

// Element with same ID is not appended:
users.append(User::new("u_42", "Tom Mervolo Dolder"));
assert_eq!(
    users.elements(),
    [
        User::new("u_42", "Satoshi Nakamoto"),
        User::new("u_1337", "Leia Skywalker"),
        User::new("u_237", "Alan Turing"),
    ]
    .iter()
    .collect::<Vec<&User>>()
);

update_or_insert

// Element with same ID replaces existing if an `update_*` method is used:
// e.g. `update_or_insert`:
users.update_or_insert(User::new("u_42", "Tom Mervolo Dolder"), 0);
assert_eq!(
    users.elements(),
    [
        User::new("u_42", "Tom Mervolo Dolder"),
        User::new("u_1337", "Leia Skywalker"),
        User::new("u_237", "Alan Turing"),
    ]
    .iter()
    .collect::<Vec<&User>>()
);

update_or_append

// or `update_or_append`
users.update_or_append(User::new("u_237", "Marie Curie"));
assert_eq!(
    users.elements(),
    [
        User::new("u_42", "Tom Mervolo Dolder"),
        User::new("u_1337", "Leia Skywalker"),
        User::new("u_237", "Marie Curie"),
    ]
    .iter()
    .collect::<Vec<&User>>()
);

Or you can provide a closure that describes an element's identity:

let numbers = IdentifiedVec::<u32, u32>::new_identifying_element(|e| *e);

Motivation

None of the std collections BTreeSet and HashSet retain insertion order, Vec retains insertion order, however, it allows for duplicates. So if you want a collection of unique elements (Set-like) that does retain insertion order, IdentifiedVec suits your needs. Even better, the elements does not need to be to impl Hash nor Ord.

Flags

This crate has the following Cargo features:

  • serde: Enables serde serialization support on IdentifiedVecOf type (which Element impl Identifiable trait).
  • id_prim: Get impl of trait Identifiable for primitives: i8,.., i128, u8, ..., u128 and bool (not so useful, allows for only two elements in IdentifiedVecOf, but who am I to discriminate.)

Implementation Details

An identified vec consists of a Vec of IDs keeping insertion order and a HashMap of id-element pairs, for constant time lookup of element given an ID.

License

Licensed under MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)

Dependencies

~0.3–0.9MB
~20K SLoC