ndhistogram/axis/
category.rs1use std::hash::Hash;
2use std::{collections::HashMap, fmt::Display};
3
4use super::Axis;
5use super::SingleValueBinInterval;
6
7pub trait Value: Eq + Hash + Clone {}
9impl<T: Eq + Hash + Clone> Value for T {}
10
11#[derive(Default, Clone, PartialEq, Eq, Debug)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct Category<T>
30where
31 T: Eq + Hash,
32{
33 map_t_to_index: HashMap<T, usize>,
34 map_index_to_t: HashMap<usize, T>,
35}
36
37impl<T: Value> Category<T> {
38 fn insert(&mut self, value: T) {
39 let index = self.map_index_to_t.len();
41 self.map_index_to_t.insert(index, value.clone());
42 self.map_t_to_index.insert(value, index);
43 }
44
45 fn get_index(&self, value: &T) -> Option<usize> {
46 self.map_t_to_index.get(value).copied()
47 }
48
49 fn get_value(&self, index: usize) -> Option<&T> {
50 self.map_index_to_t.get(&index)
51 }
52
53 fn len(&self) -> usize {
54 self.map_index_to_t.len()
55 }
56
57 fn constructor<I: IntoIterator<Item = T>>(values: I) -> Self {
58 let mut cat = Self {
59 map_t_to_index: HashMap::new(),
60 map_index_to_t: HashMap::new(),
61 };
62 values.into_iter().for_each(|it| cat.insert(it));
64 cat
65 }
66
67 pub fn new<I: IntoIterator<Item = T>>(values: I) -> Self {
72 Self::constructor(values)
73 }
74}
75
76impl<T: Value> Axis for Category<T> {
77 type Coordinate = T;
78
79 type BinInterval = SingleValueBinInterval<T>;
80
81 #[inline]
82 fn index(&self, coordinate: &Self::Coordinate) -> Option<usize> {
83 self.get_index(coordinate).or_else(|| Some(self.len()))
84 }
85
86 fn num_bins(&self) -> usize {
87 self.len() + 1
88 }
89
90 fn bin(&self, index: usize) -> Option<Self::BinInterval> {
91 let value = self.get_value(index);
92 match value {
93 Some(value) => Some(Self::BinInterval::new(value.clone())),
94 None => {
95 if index == self.len() {
96 Some(Self::BinInterval::overflow())
97 } else {
98 None
99 }
100 }
101 }
102 }
103}
104
105impl<T: Display + Value> Display for Category<T> {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 let comma_separated_list = self
108 .bins()
109 .take(10)
110 .map(|it| it.to_string())
111 .collect::<Vec<_>>()
112 .join(", ");
113 write!(f, "{{{}}}", comma_separated_list)
114 }
115}
116
117impl<'a, T: Value> IntoIterator for &'a Category<T> {
118 type Item = (usize, <Category<T> as Axis>::BinInterval);
119 type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
120
121 fn into_iter(self) -> Self::IntoIter {
122 self.iter()
123 }
124}