Skip to content

Commit

Permalink
Update README and doctests
Browse files Browse the repository at this point in the history
  • Loading branch information
Sajjon committed Dec 9, 2023
1 parent 5603263 commit 18afc84
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 16 deletions.
103 changes: 94 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
# `identified_vec`
An collection of unique identifiable elements which retains **insertion** order, inspired by [Pointfree's Swift Identified Collections](https://github.com/pointfreeco/swift-identified-collections).

Similar to the standard `Vec`, identified vecs maintain their elements in a particular user-specified order. However, unlike `Vec`, `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.
[![codecov](https://codecov.io/github/Sajjon/identified_vec/graph/badge.svg?token=Em6TayrP8j)](https://codecov.io/github/Sajjon/identified_vec)

A collection of unique identifiable elements which retains **insertion** order, inspired by [Pointfree's Swift Identified Collections](https://github.com/pointfreeco/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.

```rust
extern crate identified_vec;
use identified_vec::identified_vec::IdentifiedVec;
use identified_vec::identifiable::Identifiable;
use identified_vec::identified_vec_of::IdentifiedVecOf;
use std::cell::RefCell;

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

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

Expand All @@ -30,12 +42,85 @@ impl Identifiable for User {
}
}

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

assert_eq!(users.index_of_id(&"u_1729"), Some(1));
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")
);

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>>()
);

// 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>>()
);

// 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 mutate with `get_mut(id)`
*users.get_mut(&"u_1337").unwrap().name.get_mut() = "Yoda";
assert_eq!(
users.elements(),
[
User::new("u_42", "Tom Mervolo Dolder"),
User::new("u_1337", "Yoda"),
User::new("u_237", "Marie Curie"),
]
.iter()
.collect::<Vec<&User>>()
);
```

Or you can provide a closure that describes an element's identity:
Expand Down
95 changes: 88 additions & 7 deletions src/identified_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,23 @@ pub enum ConflictResolutionChoice {
/// use identified_vec::identified_vec::IdentifiedVec;
/// use identified_vec::identifiable::Identifiable;
/// use identified_vec::identified_vec_of::IdentifiedVecOf;
/// use std::cell::RefCell;
///
/// #[derive(Eq, PartialEq, Clone, Debug, Hash)]
/// #[derive(Eq, PartialEq, Clone, Debug)]
/// struct User {
/// id: &'static str,
/// name: RefCell<&'static str>,
/// }
///
/// impl User {
/// fn new(id: &'static str) -> Self {
/// Self { id }
/// fn new(id: &'static str, name: &'static str) -> Self {
/// Self {
/// id,
/// name: RefCell::new(name),
/// }
/// }
/// fn name(&self) -> &'static str {
/// *self.name.borrow()
/// }
/// }
///
Expand All @@ -55,10 +63,83 @@ pub enum ConflictResolutionChoice {
/// }
/// }
///
/// let mut users =
/// IdentifiedVecOf::<User>::from_iter([User::new("u_42"), User::new("u_1729")]);
/// 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")
/// );
/// 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>>()
/// );
///
/// // 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>>()
/// );
///
/// // 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>>()
/// );
///
/// assert_eq!(users.index_of_id(&"u_1729"), Some(1));
/// // or mutate with `get_mut(id)`
/// *users.get_mut(&"u_1337").unwrap().name.get_mut() = "Yoda";
/// assert_eq!(
/// users.elements(),
/// [
/// User::new("u_42", "Tom Mervolo Dolder"),
/// User::new("u_1337", "Yoda"),
/// User::new("u_237", "Marie Curie"),
/// ]
/// .iter()
/// .collect::<Vec<&User>>()
/// );
/// ```
///
/// Or you can provide a closure that describes an element's identity:
Expand Down Expand Up @@ -796,7 +877,7 @@ where
#[cfg(test)]
mod tests {

use std::{cell::RefCell, collections::HashSet, fmt::Debug};
use std::{cell::RefCell, collections::HashSet, fmt::Debug, ops::Deref};

use crate::{
identifiable::Identifiable,
Expand Down

0 comments on commit 18afc84

Please sign in to comment.