chars_counter/
lib.rs

1//! The trait that implements character counting for the &str type.
2//!
3//! # Quick Start
4//! ```
5//! use chars_counter::{ICharsCounter, ICharCounterExt};
6//!
7//! let str = "Hello world!";
8//! let result = str.count_chars();
9//! // result = [CharsCounter { character: 'l', count: 3 }, CharsCounter { character: 'o', count: 2 }, CharsCounter { character: ' ', count: 1 }, CharsCounter { character: '!', count: 1 }, CharsCounter { character: 'H', count: 1 }, CharsCounter { character: 'd', count: 1 }, CharsCounter { character: 'e', count: 1 }, CharsCounter { character: 'r', count: 1 }, CharsCounter { character: 'w', count: 1 }]
10//!
11//! // You can also use like this:
12//! let result = str.count_chars_numeric();
13//! let result = str.count_chars_alphabetic();
14//! let result = str.count_chars_chinese();
15//! // ...... Others you can try by yourself.
16//! // if those can't meet your needs, you can custom your own rules by
17//! let result = str.count_chars_filter(|x| *x != ' '); // ignore whitespaces.
18//!
19//! // More features:
20//! let result = str.count_chars().most_chars();
21//! // result = [CharsCounter { character: 'l', count: 3 }]
22//! let result = str.count_chars().least_chars();
23//! // result = [CharsCounter { character: ' ', count: 1 }, CharsCounter { character: '!', count: 1 }, CharsCounter { character: 'H', count: 1 }, CharsCounter { character: 'd', count: 1 }, CharsCounter { character: 'e', count: 1 }, CharsCounter { character: 'r', count: 1 }, CharsCounter { character: 'w', count: 1 }]
24//! let result = str.count_chars().find_by_char('l');
25//! // result = Some(CharsCounter { character: 'l', count: 3 })
26//! let result = str.count_chars().find_by_num(2);
27//! // result = [CharsCounter { character: 'o', count: 2 }]
28//! let result = str.count_chars().least_chars().find_by_char('H');
29//! // result = Some(CharsCounter { character: 'H', count: 1 })
30//! ```
31
32use itertools::Itertools;
33
34#[derive(Clone, Copy, PartialEq, Debug)]
35pub struct CharsCounter {
36    pub character: char,
37    pub count: usize,
38}
39
40pub trait ICharsCounter {
41    fn count_chars(&self) -> Vec<CharsCounter>;
42    fn count_chars_ascii(&self) -> Vec<CharsCounter>;
43    fn count_chars_numeric(&self) -> Vec<CharsCounter>;
44    fn count_chars_alphabetic(&self) -> Vec<CharsCounter>;
45    fn count_chars_alphanumeric(&self) -> Vec<CharsCounter>;
46    fn count_chars_whitespace(&self) -> Vec<CharsCounter>;
47    fn count_chars_no_whitespace(&self) -> Vec<CharsCounter>;
48    fn count_chars_chinese(&self) -> Vec<CharsCounter>;
49
50    fn count_chars_filter<P>(&self, predicate: P) -> Vec<CharsCounter>
51    where
52        P: FnMut(&char) -> bool;
53}
54
55pub trait ICharCounterExt {
56    fn most_chars(&self) -> Vec<CharsCounter>;
57    fn least_chars(&self) -> Vec<CharsCounter>;
58    fn find_by_num(&self, n: usize) -> Vec<CharsCounter>;
59    fn find_by_char(&self, c: char) -> Option<CharsCounter>;
60    fn counter_filter<P>(&self, predicate: P) -> Vec<CharsCounter>
61    where
62        P: FnMut(&&CharsCounter) -> bool;
63}
64
65impl ICharsCounter for &str {
66    fn count_chars(&self) -> Vec<CharsCounter> {
67        self.count_chars_filter(|_| true)
68    }
69
70    fn count_chars_ascii(&self) -> Vec<CharsCounter> {
71        self.count_chars_filter(|x| x.is_ascii())
72    }
73
74    fn count_chars_numeric(&self) -> Vec<CharsCounter> {
75        self.count_chars_filter(|x| x.is_numeric())
76    }
77
78    fn count_chars_alphabetic(&self) -> Vec<CharsCounter> {
79        self.count_chars_filter(|x| x.is_alphabetic())
80    }
81
82    fn count_chars_alphanumeric(&self) -> Vec<CharsCounter> {
83        self.count_chars_filter(|x| x.is_alphanumeric())
84    }
85
86    fn count_chars_whitespace(&self) -> Vec<CharsCounter> {
87        self.count_chars_filter(|x| x.is_whitespace())
88    }
89
90    fn count_chars_no_whitespace(&self) -> Vec<CharsCounter> {
91        self.count_chars_filter(|x| *x != ' ')
92    }
93
94    fn count_chars_chinese(&self) -> Vec<CharsCounter> {
95        self.count_chars_filter(|x| *x as u32 >= 19968 && *x as u32 <= 40959)
96    }
97
98    fn count_chars_filter<P>(&self, predicate: P) -> Vec<CharsCounter>
99    where
100        P: FnMut(&char) -> bool,
101    {
102        self.chars()
103            .filter(predicate)
104            .into_group_map_by(|&x| x)
105            .into_iter()
106            .map(|x| CharsCounter {
107                character: x.0,
108                count: x.1.len(),
109            })
110            .sorted_by(|x, y| y.count.cmp(&x.count).then(x.character.cmp(&y.character)))
111            .collect::<Vec<_>>()
112    }
113}
114
115impl ICharCounterExt for Vec<CharsCounter> {
116    fn most_chars(&self) -> Vec<CharsCounter> {
117        self.counter_filter(|x| x.count == self[0].count)
118    }
119
120    fn least_chars(&self) -> Vec<CharsCounter> {
121        self.counter_filter(|x| x.count == self[self.len() - 1].count)
122    }
123
124    fn find_by_num(&self, n: usize) -> Vec<CharsCounter> {
125        self.counter_filter(|x| x.count == n)
126    }
127
128    fn find_by_char(&self, c: char) -> Option<CharsCounter> {
129        self.iter().find(|x| x.character == c).map(|&x| x)
130    }
131
132    fn counter_filter<P>(&self, predicate: P) -> Vec<CharsCounter>
133    where
134        P: FnMut(&&CharsCounter) -> bool,
135    {
136        self.iter()
137            .filter(predicate)
138            .map(|&x| x)
139            .collect::<Vec<_>>()
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use crate::{CharsCounter, ICharCounterExt, ICharsCounter};
146
147    #[test]
148    fn most_chars_test() {
149        let str = "Hello world!";
150        let result = str.count_chars().most_chars();
151        assert_eq!(
152            result[0],
153            CharsCounter {
154                character: 'l',
155                count: 3
156            }
157        );
158    }
159
160    #[test]
161    fn least_chars_test() {
162        let str = "Hello world!";
163        let result = str.count_chars().least_chars();
164        assert_eq!(
165            result[0],
166            CharsCounter {
167                character: ' ',
168                count: 1
169            }
170        );
171    }
172
173    #[test]
174    fn find_by_num_test() {
175        let str = "Hello world!";
176        let result = str.count_chars().find_by_num(2);
177        assert_eq!(
178            result[0],
179            CharsCounter {
180                character: 'o',
181                count: 2
182            }
183        );
184    }
185
186    #[test]
187    fn find_by_char_test() {
188        let str = "Hello world!";
189        let result = str.count_chars().find_by_char('H').unwrap();
190        assert_eq!(
191            result,
192            CharsCounter {
193                character: 'H',
194                count: 1
195            }
196        );
197    }
198}