{"id":17302347,"url":"https://github.com/coriolinus/counter-rs","last_synced_at":"2025-04-09T15:08:11.362Z","repository":{"id":15352041,"uuid":"77961070","full_name":"coriolinus/counter-rs","owner":"coriolinus","description":"Simple object to count Rust iterables","archived":false,"fork":false,"pushed_at":"2024-06-30T11:25:44.000Z","size":121,"stargazers_count":28,"open_issues_count":5,"forks_count":21,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-09T15:08:06.265Z","etag":null,"topics":["bag","count","counter","hacktoberfest","multiset","rust"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/counter","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coriolinus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-01-03T23:09:32.000Z","updated_at":"2025-03-06T05:35:18.000Z","dependencies_parsed_at":"2023-09-29T07:58:38.925Z","dependency_job_id":null,"html_url":"https://github.com/coriolinus/counter-rs","commit_stats":{"total_commits":88,"total_committers":13,"mean_commits":6.769230769230769,"dds":0.3977272727272727,"last_synced_commit":"155e50b6a7ec6ec9b44562cfe9de78c293a05073"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coriolinus%2Fcounter-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coriolinus%2Fcounter-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coriolinus%2Fcounter-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coriolinus%2Fcounter-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coriolinus","download_url":"https://codeload.github.com/coriolinus/counter-rs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248055284,"owners_count":21040157,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bag","count","counter","hacktoberfest","multiset","rust"],"created_at":"2024-10-15T11:47:16.111Z","updated_at":"2025-04-09T15:08:11.343Z","avatar_url":"https://github.com/coriolinus.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# counter\n\nCounter counts recurrent elements of iterables. It is based on [the Python\nimplementation](https://docs.python.org/3/library/collections.html#collections.Counter).\n\nThe struct [`Counter`](struct.Counter.html) is the entry-point type for this module.\n\n## Math Underpinnings\n\nMathematically, a `Counter` implements a hash-based version of a [multiset],\nor bag. This is simply an extension of the notion of a set to the idea that\nwe care not only about whether an entity exists within the set, but the number\nof occurrences within the set. Normal set operations such as intersection,\nunion, etc. are of course still supported.\n\n[multiset]: https://en.wikipedia.org/wiki/Set_(abstract_data_type)#Multiset\n\n## Cargo Features\n\n- `serde` implements `serde::Serialize` and `serde::Deserialize` for `Counter`.\n\n## Examples\n\n### Just count an iterable\n\n```rust\nuse counter::Counter;\nlet char_counts = \"barefoot\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nlet counts_counts = char_counts.values().collect::\u003cCounter\u003c_\u003e\u003e();\n```\n\n### Update a count\n\n```rust\nlet mut counts = \"aaa\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\ncounts[\u0026'a'] += 1;\ncounts[\u0026'b'] += 1;\n```\n\n```rust\nlet mut counts = \"able babble table babble rabble table able fable scrabble\"\n    .split_whitespace().collect::\u003cCounter\u003c_\u003e\u003e();\n// add or subtract an iterable of the same type\ncounts += \"cain and abel fable table cable\".split_whitespace();\n// or add or subtract from another Counter of the same type\nlet other_counts = \"scrabble cabbie fable babble\"\n    .split_whitespace().collect::\u003cCounter\u003c_\u003e\u003e();\nlet difference = counts - other_counts;\n```\n\nExtend a `Counter` with another `Counter`:\n```rust\nlet mut counter = \"abbccc\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nlet another = \"bccddd\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\ncounter.extend(\u0026another);\nlet expect = [('a', 1), ('b', 3), ('c', 5), ('d', 3)].iter()\n    .cloned().collect::\u003cHashMap\u003c_, _\u003e\u003e();\nassert_eq!(counter.into_map(), expect);\n```\n### Get items with keys\n\n```rust\nlet counts = \"aaa\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nassert_eq!(counts[\u0026'a'], 3);\nassert_eq!(counts[\u0026'b'], 0);\n```\n\n### Get the most common items\n\n[`most_common_ordered()`] uses the natural ordering of keys which are [`Ord`].\n\n[`most_common_ordered()`]: Counter::most_common_ordered\n[`Ord`]: https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html\n\n```rust\nlet by_common = \"eaddbbccc\".chars().collect::\u003cCounter\u003c_\u003e\u003e().most_common_ordered();\nlet expected = vec![('c', 3), ('b', 2), ('d', 2), ('a', 1), ('e', 1)];\nassert!(by_common == expected);\n```\n\n[`k_most_common_ordered()`] takes an argument `k` of type `usize` and returns the top `k` most\ncommon items.  This is functionally equivalent to calling `most_common_ordered()` and then\ntruncating the result to length `k`.  However, if `k` is smaller than the length of the counter\nthen `k_most_common_ordered()` can be more efficient, often much more so.\n\n```rust\nlet by_common = \"eaddbbccc\".chars().collect::\u003cCounter\u003c_\u003e\u003e().k_most_common_ordered(2);\nlet expected = vec![('c', 3), ('b', 2)];\nassert!(by_common == expected);\n```\n\n[`k_most_common_ordered()`]: Counter::k_most_common_ordered\n[`most_common_ordered()`]: Counter::most_common_ordered\n\n### Get the most common items using your own ordering\n\nFor example, here we break ties reverse alphabetically.\n\n```rust\nlet counter = \"eaddbbccc\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nlet by_common = counter.most_common_tiebreaker(|\u0026a, \u0026b| b.cmp(\u0026a));\nlet expected = vec![('c', 3), ('d', 2), ('b', 2), ('e', 1), ('a', 1)];\nassert!(by_common == expected);\n```\n\n### Test counters against another\n\nCounters are multi-sets and so can be sub- or supersets of each other.\n\nA counter is a _subset_ of another if for all its elements, the other\ncounter has an equal or higher count. Test for this with [`is_subset()`]:\n\n```rust\nlet counter = \"aaabb\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nlet superset = \"aaabbbc\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nlet not_a_superset = \"aaae\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nassert!(counter.is_subset(\u0026superset));\nassert!(!counter.is_subset(\u0026not_a_superset));\n```\n\nTesting for a _superset_ is the inverse, [`is_superset()`] is true if the counter can contain another counter in its entirety:\n\n```rust\nlet counter = \"aaabbbc\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nlet subset = \"aabbb\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nlet not_a_subset = \"aaae\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nassert!(counter.is_superset(\u0026subset));\nassert!(!counter.is_superset(\u0026not_a_subset));\n```\n\nThese relationships continue to work when [using a _signed_ integer type for the counter][signed]: all values in the subset must be equal or lower to the values in the superset. Negative\nvalues are interpreted as 'missing' those values, and the subset would need to miss those\nsame elements, or be short more, to still be a subset:\n\n```rust\nlet mut subset = \"aaabb\".chars().collect::\u003cCounter\u003c_, i8\u003e\u003e();\nsubset.insert('e', -2);  // short 2 'e's\nsubset.insert('f', -1);  // and 1 'f'\nlet mut superset = \"aaaabbb\".chars().collect::\u003cCounter\u003c_, i8\u003e\u003e();\nsuperset.insert('e', -1);  // short 1 'e'\nassert!(subset.is_subset(\u0026superset));\nassert!(superset.is_superset(\u0026subset));\n```\n\n[`is_subset()`]: Counter::is_subset\n[`is_superset()`]: Counter::is_superset\n[signed]: #use-your-own-type-for-the-count\n\n### Counter intersection and union\n\nYou can intersect two counters, giving you the minimal counts of their\ncombined elements using the [`\u0026` bitwise and operator][BitAnd], and produce\ntheir union with the maximum counts using [`|` bitwise or][BitOr]:\n\n```rust\nlet a = \"aaabb\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nlet b = \"aabbbbe\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\n\nlet intersection = a \u0026 b;\nlet expected_intersection = \"aabb\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nassert_eq!(intersection, expected_intersection);\n\nlet c = \"aaabb\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nlet d = \"aabbbbe\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\n\nlet union = c | d;\nlet expected_union = \"aaabbbbe\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nassert_eq!(union, expected_union)\n```\n\nThe in-place [`\u0026=`] and [`|=`] operations are also supported.\n\n[BitAnd]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html\n[BitOr]: https://doc.rust-lang.org/std/ops/trait.BitOr.html\n[`\u0026=`]: https://doc.rust-lang.org/std/ops/trait.BitAndAssign.html\n[`|=`]: https://doc.rust-lang.org/std/ops/trait.BitOrAssign.html\n\n### Treat it like a `HashMap`\n\n`Counter\u003cT, N\u003e` implements [`Deref`]`\u003cTarget=HashMap\u003cT, N\u003e\u003e` and\n[`DerefMut`]`\u003cTarget=HashMap\u003cT, N\u003e\u003e`, which means that you can perform any operations\non it which are valid for a [`HashMap`].\n\n[`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html\n[`Deref`]: https://doc.rust-lang.org/stable/std/ops/trait.Deref.html\n[`DerefMut`]: https://doc.rust-lang.org/stable/std/ops/trait.DerefMut.html\n\n```rust\nlet mut counter = \"aa-bb-cc\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\ncounter.remove(\u0026'-');\nassert!(counter == \"aabbcc\".chars().collect::\u003cCounter\u003c_\u003e\u003e());\n```\n\nNote that `Counter\u003cT, N\u003e` itself implements [`Index`]. `Counter::index` returns a reference to\na [`Zero::zero`] value for missing keys.\n\n[`Index`]: https://doc.rust-lang.org/stable/std/ops/trait.Index.html\n[`Zero::zero`]: https://docs.rs/num-traits/latest/num_traits/identities/trait.Zero.html#tymethod.zero\n\n```rust\nlet counter = \"aaa\".chars().collect::\u003cCounter\u003c_\u003e\u003e();\nassert_eq!(counter[\u0026'b'], 0);\n// panics\n// assert_eq!((*counter)[\u0026'b'], 0);\n```\n\n## Advanced Usage\n\n### Count any iterable which is `Hash + Eq`\n\nYou can't use the `most_common*` functions unless `T` is also [`Clone`], but simple counting\nworks fine on a minimal data type.\n\n[`Clone`]: https://doc.rust-lang.org/stable/std/clone/trait.Clone.html\n\n```rust\n#[derive(Debug, Hash, PartialEq, Eq)]\nstruct Inty {\n    i: usize,\n}\n\nimpl Inty {\n    pub fn new(i: usize) -\u003e Inty {\n        Inty { i: i }\n    }\n}\n\n// \u003chttps://en.wikipedia.org/wiki/867-5309/Jenny\u003e\nlet intys = vec![\n    Inty::new(8),\n    Inty::new(0),\n    Inty::new(0),\n    Inty::new(8),\n    Inty::new(6),\n    Inty::new(7),\n    Inty::new(5),\n    Inty::new(3),\n    Inty::new(0),\n    Inty::new(9),\n];\n\nlet inty_counts = intys.iter().collect::\u003cCounter\u003c_\u003e\u003e();\nprintln!(\"{:?}\", inty_counts);\n// {Inty { i: 8 }: 2, Inty { i: 0 }: 3, Inty { i: 9 }: 1, Inty { i: 3 }: 1,\n//  Inty { i: 7 }: 1, Inty { i: 6 }: 1, Inty { i: 5 }: 1}\nassert!(inty_counts.get(\u0026Inty { i: 8 }) == Some(\u00262));\nassert!(inty_counts.get(\u0026Inty { i: 0 }) == Some(\u00263));\nassert!(inty_counts.get(\u0026Inty { i: 6 }) == Some(\u00261));\n```\n\n### Use your own type for the count\n\nSometimes [`usize`] just isn't enough. If you find yourself overflowing your\nmachine's native size, you can use your own type. Here, we use an [`i8`], but\nyou can use most numeric types, including bignums, as necessary.\n\n[`usize`]: https://doc.rust-lang.org/stable/std/primitive.usize.html\n[`i8`]: https://doc.rust-lang.org/stable/std/primitive.i8.html\n\n```rust\nlet counter: Counter\u003c_, i8\u003e = \"abbccc\".chars().collect();\nlet expected: HashMap\u003cchar, i8\u003e = [('a', 1), ('b', 2), ('c', 3)].iter().cloned().collect();\nassert!(counter.into_map() == expected);\n```\n\nLicense: MIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoriolinus%2Fcounter-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoriolinus%2Fcounter-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoriolinus%2Fcounter-rs/lists"}