20 个版本

0.6.0 2024年6月30日
0.5.7 2022年10月12日
0.5.6 2022年7月16日
0.5.3 2022年2月7日
0.1.0 2017年1月4日

142数据结构 中排名

Download history 14441/week @ 2024-05-03 14613/week @ 2024-05-10 14080/week @ 2024-05-17 12306/week @ 2024-05-24 12903/week @ 2024-05-31 14700/week @ 2024-06-07 14448/week @ 2024-06-14 11297/week @ 2024-06-21 10925/week @ 2024-06-28 10330/week @ 2024-07-05 11704/week @ 2024-07-12 13040/week @ 2024-07-19 17957/week @ 2024-07-26 19460/week @ 2024-08-02 18251/week @ 2024-08-09 18377/week @ 2024-08-16

77,194 每月下载量
用于 73 包(直接使用 18 个)

MIT 许可

64KB
775

counter

Counter 计数迭代器的重复元素。它基于 Python 实现

Counter 是此模块的入口点类型。

数学基础

从数学的角度看,Counter 实现了基于哈希的多重集或包的版本。这仅仅是集合概念的扩展,我们不仅关心实体是否存在于集合中,还关心它在集合中的出现次数。交集、并集等常规集合运算当然仍然支持。

Cargo 功能

  • serdeCounter 实现了 serde::Serializeserde::Deserialize

示例

仅计数一个迭代器

use counter::Counter;
let char_counts = "barefoot".chars().collect::<Counter<_>>();
let counts_counts = char_counts.values().collect::<Counter<_>>();

更新计数

let mut counts = "aaa".chars().collect::<Counter<_>>();
counts[&'a'] += 1;
counts[&'b'] += 1;
let mut counts = "able babble table babble rabble table able fable scrabble"
    .split_whitespace().collect::<Counter<_>>();
// add or subtract an iterable of the same type
counts += "cain and abel fable table cable".split_whitespace();
// or add or subtract from another Counter of the same type
let other_counts = "scrabble cabbie fable babble"
    .split_whitespace().collect::<Counter<_>>();
let difference = counts - other_counts;

用另一个 Counter 扩展 Counter

let mut counter = "abbccc".chars().collect::<Counter<_>>();
let another = "bccddd".chars().collect::<Counter<_>>();
counter.extend(&another);
let expect = [('a', 1), ('b', 3), ('c', 5), ('d', 3)].iter()
    .cloned().collect::<HashMap<_, _>>();
assert_eq!(counter.into_map(), expect);

根据键获取项目

let counts = "aaa".chars().collect::<Counter<_>>();
assert_eq!(counts[&'a'], 3);
assert_eq!(counts[&'b'], 0);

获取最常见的项目

most_common_ordered() 使用键的自然排序,这些键是 Ord

let by_common = "eaddbbccc".chars().collect::<Counter<_>>().most_common_ordered();
let expected = vec![('c', 3), ('b', 2), ('d', 2), ('a', 1), ('e', 1)];
assert!(by_common == expected);

k_most_common_ordered() 接受一个类型为 usize 的参数 k 并返回最常见的 top k 个项目。这与调用 most_common_ordered() 然后截断结果到长度 k 是功能上等效的。然而,如果 k 小于计数器的长度,则 k_most_common_ordered() 可以更高效,通常要高效得多。

let by_common = "eaddbbccc".chars().collect::<Counter<_>>().k_most_common_ordered(2);
let expected = vec![('c', 3), ('b', 2)];
assert!(by_common == expected);

使用自己的排序方式获取最常出现的项

例如,在这里我们按反字母顺序解决平局。

let counter = "eaddbbccc".chars().collect::<Counter<_>>();
let by_common = counter.most_common_tiebreaker(|&a, &b| b.cmp(&a));
let expected = vec![('c', 3), ('d', 2), ('b', 2), ('e', 1), ('a', 1)];
assert!(by_common == expected);

测试计数器与另一个计数器

计数器是多集,因此可以是彼此的子集或超集。

如果一个计数器的所有元素在另一个计数器中的计数相等或更高,那么这个计数器是另一个计数器的子集。使用以下方法测试这一情况:is_subset()

let counter = "aaabb".chars().collect::<Counter<_>>();
let superset = "aaabbbc".chars().collect::<Counter<_>>();
let not_a_superset = "aaae".chars().collect::<Counter<_>>();
assert!(counter.is_subset(&superset));
assert!(!counter.is_subset(&not_a_superset));

检查超集是逆操作,如果计数器可以完整包含另一个计数器,则is_superset()为真。is_superset()

let counter = "aaabbbc".chars().collect::<Counter<_>>();
let subset = "aabbb".chars().collect::<Counter<_>>();
let not_a_subset = "aaae".chars().collect::<Counter<_>>();
assert!(counter.is_superset(&subset));
assert!(!counter.is_superset(&not_a_subset));

当使用有符号整数类型作为计数器时,这些关系仍然有效:子集中的所有值必须等于或低于超集中的值。负值被解释为“缺少”这些值,子集需要缺少相同的元素,或者比超集短,才能仍然是子集。

let mut subset = "aaabb".chars().collect::<Counter<_, i8>>();
subset.insert('e', -2);  // short 2 'e's
subset.insert('f', -1);  // and 1 'f'
let mut superset = "aaaabbb".chars().collect::<Counter<_, i8>>();
superset.insert('e', -1);  // short 1 'e'
assert!(subset.is_subset(&superset));
assert!(superset.is_superset(&subset));

计数器交集和并集

您可以使用&位与运算符将两个计数器相交,以获得它们组合元素的最小计数,并使用|位或运算符生成它们的并集,以获得最大计数。

let a = "aaabb".chars().collect::<Counter<_>>();
let b = "aabbbbe".chars().collect::<Counter<_>>();

let intersection = a & b;
let expected_intersection = "aabb".chars().collect::<Counter<_>>();
assert_eq!(intersection, expected_intersection);

let c = "aaabb".chars().collect::<Counter<_>>();
let d = "aabbbbe".chars().collect::<Counter<_>>();

let union = c | d;
let expected_union = "aaabbbbe".chars().collect::<Counter<_>>();
assert_eq!(union, expected_union)

还支持就地&=|=操作。

将其视为HashMap

Counter<T, N>实现了Deref<Target=HashMap<T, N>>DerefMut<Target=HashMap<T, N>>,这意味着您可以对它执行任何对HashMap有效的操作。

let mut counter = "aa-bb-cc".chars().collect::<Counter<_>>();
counter.remove(&'-');
assert!(counter == "aabbcc".chars().collect::<Counter<_>>());

请注意,Counter<T, N>本身实现了IndexCounter::index返回一个指向Zero::zero值的引用,用于缺失的键。

let counter = "aaa".chars().collect::<Counter<_>>();
assert_eq!(counter[&'b'], 0);
// panics
// assert_eq!((*counter)[&'b'], 0);

高级用法

计数任何可迭代的元素,这些元素是Hash + Eq

除非T也是Clone,否则您不能使用most_common*函数,但简单的计数在最小数据类型上运行良好。

#[derive(Debug, Hash, PartialEq, Eq)]
struct Inty {
    i: usize,
}

impl Inty {
    pub fn new(i: usize) -> Inty {
        Inty { i: i }
    }
}

// <https://en.wikipedia.org/wiki/867-5309/Jenny>
let intys = vec![
    Inty::new(8),
    Inty::new(0),
    Inty::new(0),
    Inty::new(8),
    Inty::new(6),
    Inty::new(7),
    Inty::new(5),
    Inty::new(3),
    Inty::new(0),
    Inty::new(9),
];

let inty_counts = intys.iter().collect::<Counter<_>>();
println!("{:?}", inty_counts);
// {Inty { i: 8 }: 2, Inty { i: 0 }: 3, Inty { i: 9 }: 1, Inty { i: 3 }: 1,
//  Inty { i: 7 }: 1, Inty { i: 6 }: 1, Inty { i: 5 }: 1}
assert!(inty_counts.get(&Inty { i: 8 }) == Some(&2));
assert!(inty_counts.get(&Inty { i: 0 }) == Some(&3));
assert!(inty_counts.get(&Inty { i: 6 }) == Some(&1));

使用自己的类型进行计数

有时,`usize` 并不能满足需求。如果你发现自己的机器原生大小已溢出,可以使用自己的类型。在这里,我们使用一个 `i8`,但根据需要,可以使用大多数数值类型,包括大数。

let counter: Counter<_, i8> = "abbccc".chars().collect();
let expected: HashMap<char, i8> = [('a', 1), ('b', 2), ('c', 3)].iter().cloned().collect();
assert!(counter.into_map() == expected);

许可证:MIT

依赖项

~94–320KB