Last active
June 24, 2024 23:10
-
-
Save Rudxain/f9c8d3764e342b8234aaaeb62e2190f5 to your computer and use it in GitHub Desktop.
Counts how many instances of each value are present in an iterable, implemented in Rust and TypeScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const count_values = <T,>(it: Iterable<T>) => { | |
const counts: Map<T, bigint> = new Map | |
for (const x of it) | |
counts.set(x, (counts.get(x) ?? 0n) + 1n) | |
return counts | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::{collections::HashMap, hash::Hash}; | |
use num_bigint::BigUint; // 0.4 | |
use num_integer::Integer; // 0.1 | |
use num_traits::One; // 0.2 | |
/* | |
`HashMap` is allocated inside the fns, | |
because further allocs will eventually happen, | |
as it's impossible to infer the final size before iterating. | |
This is the same reason why we use `new` instead of `with_capacity`. | |
There's no benefit in forcing the caller to pre-alloc. | |
In fact, if the caller passes an existing `HashMap`, | |
it may be "dirty" (intentionally or not), | |
making the fns impure. | |
`T` is preferred over `&'a T`, | |
because it allows the caller to choose | |
any `HashMap` key type | |
*/ | |
/// `ExactSizeIterator` is necessary | |
/// to guarantee the counters won't overflow | |
pub fn count_values_sized<I, T>(it: I) -> HashMap<T, usize> | |
where | |
I: IntoIterator<Item = T>, | |
I::IntoIter: ExactSizeIterator, | |
T: Eq + Hash, | |
{ | |
let mut counts = HashMap::new(); | |
for x in it { | |
counts | |
.entry(x) | |
// why `checked_add` causes inference to totally fail? | |
.and_modify(|c: &mut usize| *c = (*c).checked_add(1).unwrap_or_else(|| unreachable!())) | |
.or_insert(1); | |
} | |
counts | |
} | |
pub fn count_values_unbounded<I, T>(it: I) -> HashMap<T, BigUint> | |
where | |
I: IntoIterator<Item = T>, | |
T: Eq + Hash, | |
{ | |
let mut counts = HashMap::new(); | |
for x in it { | |
counts | |
.entry(x) | |
.and_modify(|c: &mut BigUint| c.inc()) | |
.or_insert(BigUint::one()); | |
} | |
counts | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment