|
| 1 | +#[cfg(test)] |
| 2 | +mod test; |
| 3 | + |
| 4 | +/// A (multi-)map based on a sorted vector. This uses binary search to |
| 5 | +/// find the starting index for a given element and can be a fairly |
| 6 | +/// efficient data structure, particularly for small-ish sets of data. |
| 7 | +/// |
| 8 | +/// To use, you supply the starting vector along with a "key fn" that |
| 9 | +/// extracts the key from each element. |
| 10 | +pub struct VecMap<E, KeyFn> { |
| 11 | + data: Vec<E>, |
| 12 | + key_fn: KeyFn, |
| 13 | +} |
| 14 | + |
| 15 | +impl<E, K, KeyFn> VecMap<E, KeyFn> |
| 16 | +where |
| 17 | + KeyFn: Fn(&E) -> K, |
| 18 | + K: Ord + std::fmt::Debug, |
| 19 | +{ |
| 20 | + pub fn new( |
| 21 | + mut data: Vec<E>, |
| 22 | + key_fn: KeyFn, |
| 23 | + ) -> Self { |
| 24 | + data.sort_by_key(&key_fn); |
| 25 | + Self { data, key_fn } |
| 26 | + } |
| 27 | + |
| 28 | + /// Extract the first index for the given key using binary search. |
| 29 | + /// Returns `None` if there is no such index. |
| 30 | + fn get_range(&self, key: &K) -> Option<(usize, usize)> { |
| 31 | + match self.data.binary_search_by_key(key, &self.key_fn) { |
| 32 | + Ok(mid) => { |
| 33 | + // We get back *some* element with the given key -- so |
| 34 | + // search backwards to find the *first* one. |
| 35 | + // |
| 36 | + // (It'd be more efficient to use a "galloping" search |
| 37 | + // here, but it's not really worth it for small-ish |
| 38 | + // amounts of data.) |
| 39 | + let mut start = mid; |
| 40 | + while start > 0 { |
| 41 | + if (self.key_fn)(&self.data[start - 1]) == *key { |
| 42 | + start -= 1; |
| 43 | + } else { |
| 44 | + break; |
| 45 | + } |
| 46 | + } |
| 47 | + |
| 48 | + // Now search forward to find the *last* one. |
| 49 | + // |
| 50 | + // (It'd be more efficient to use a "galloping" search |
| 51 | + // here, but it's not really worth it for small-ish |
| 52 | + // amounts of data.) |
| 53 | + let mut end = mid + 1; |
| 54 | + let max = self.data.len(); |
| 55 | + while end < max { |
| 56 | + if (self.key_fn)(&self.data[end]) == *key { |
| 57 | + end += 1; |
| 58 | + } else { |
| 59 | + break; |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + Some((start, end)) |
| 64 | + } |
| 65 | + Err(_) => None, |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + /// Gets the (first) value associated with a given key. |
| 70 | + pub fn get_first(&self, key: &K) -> Option<&E> { |
| 71 | + let (start, _) = self.get_range(key)?; |
| 72 | + Some(&self.data[start]) |
| 73 | + } |
| 74 | + |
| 75 | + /// Gets a slice of values associated with the given key. |
| 76 | + pub fn get_all(&self, key: &K) -> &[E] { |
| 77 | + let (start, end) = self.get_range(key).unwrap_or((0, 0)); |
| 78 | + &self.data[start..end] |
| 79 | + } |
| 80 | + |
| 81 | + /// Gets a slice of values associated with the given key. |
| 82 | + pub fn get_iter<'k>(&'k self, key: &'k K) -> impl Iterator<Item = &'k E> { |
| 83 | + self.get_all(key).iter() |
| 84 | + } |
| 85 | +} |
0 commit comments