Skip to content

Commit

Permalink
feat: Add simple iterator for the trie
Browse files Browse the repository at this point in the history
  • Loading branch information
vemonet committed Dec 21, 2023
1 parent 3e61eb0 commit a4da3f5
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 5 deletions.
8 changes: 5 additions & 3 deletions .github/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ if [ "$#" -ne 1 ]; then
fi
new_version=$1

# Update changelog
git cliff -o CHANGELOG.md

# Update version in Cargo.toml
sed -i "s/^version = \"[0-9]*\.[0-9]*\.[0-9]*\"\$/version = \"$new_version\"/" "Cargo.toml"

# Create and push tag
git tag -a v$new_version -m "v$new_version"
git push origin v$new_version

# Update changelog
git cliff -o CHANGELOG.md
git commit -S -m "chore: Update changelog"
git push

echo "🎉 $new_version released"
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,25 @@ let strings = trie.find_postfixes("app".bytes());
assert_eq!(strings, vec!["App", "Apple", "Applet"]);
```

### 🔑 Key-based Retrieval Functions
### 🔑 Key-based retrieval functions

The crate provides functions to check for the existence of a key and to retrieve the associated value.
The crate provides functions to check for the existence of a key, to retrieve the associated value, or iterate the trie nodes.

```rust
use ptrie::Trie;

let mut trie = Trie::new();
trie.insert("app".bytes(), "App");
trie.insert("applet".bytes(), "Applet");

assert!(trie.contains_key("app".bytes()));
assert!(!trie.contains_key("not_existing_key".bytes()));
assert_eq!(trie.get_value("app".bytes()), Some("App"));
assert_eq!(trie.get_value("none".bytes()), None);

for (k, v) in trie.iter() {
println!("kv: {} {}", k, v);
}
```

## 🏷️ Features
Expand Down
62 changes: 62 additions & 0 deletions src/trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,28 @@ impl<K: Eq + Ord + Clone, V: Clone> Trie<K, V> {
self.nodes.push(TrieNode::new(None));
self.nodes.len() - 1
}

/// Iterate the nodes in the trie
///
/// # Example
///
/// ```
/// use ptrie::Trie;
///
/// let mut t = Trie::new();
/// let test = "test".bytes();
/// let tes = "tes".bytes();
///
/// t.insert(test.clone(), String::from("test"));
/// t.insert(tes.clone(), String::from("tes"));
/// for (k, v) in t.iter() {
/// assert!(std::str::from_utf8(&k).unwrap().starts_with("tes"));
/// assert!(v.starts_with("tes"));
/// }
/// ```
pub fn iter(&self) -> TrieIterator<K, V> {
TrieIterator::new(self)
}
}

/// Implement the `Default` trait for `Trie` since we have a constructor that does not need arguments
Expand All @@ -340,3 +362,43 @@ impl<T: Eq + Ord + Clone, U: Clone> Default for Trie<T, U> {
Self::new()
}
}

/// Iterator for the `Trie` struct
pub struct TrieIterator<'a, K, V> {
trie: &'a Trie<K, V>,
stack: Vec<(usize, Vec<K>)>, // Stack with node id and current path
}

impl<'a, K, V> TrieIterator<'a, K, V> {
fn new(trie: &'a Trie<K, V>) -> Self {
TrieIterator {
trie,
stack: vec![(0, Vec::new())], // Start with root node and empty path
}
}
}

impl<'a, K, V> Iterator for TrieIterator<'a, K, V>
where
K: Eq + Ord + Clone,
V: Clone,
{
type Item = (Vec<K>, V); // Yield key-value pairs

fn next(&mut self) -> Option<Self::Item> {
while let Some((node_id, mut path)) = self.stack.pop() {
let node = &self.trie.nodes[node_id];
// Push children to the stack with updated path
for &(ref key_part, child_id) in &node.children {
let mut new_path = path.clone();
new_path.push(key_part.clone());
self.stack.push((child_id, new_path));
}
// Return value if it exists
if let Some(value_id) = node.get_value() {
return Some((path, self.trie.values[value_id].clone()));
}
}
None
}
}
14 changes: 14 additions & 0 deletions tests/trie_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,18 @@ mod tests {
assert!(t.is_empty());
assert!(!t.contains_key(data));
}

#[test]
fn iterator() {
let mut t = Trie::new();
let test = "test".bytes();
let tes = "tes".bytes();

t.insert(test.clone(), String::from("test"));
t.insert(tes.clone(), String::from("tes"));
for (k, v) in t.iter() {
assert!(std::str::from_utf8(&k).unwrap().starts_with("tes"));
assert!(v.starts_with("tes"));
}
}
}

0 comments on commit a4da3f5

Please sign in to comment.