// Package batch: High-throughput username & identifier collision scanner
///|
pub(all) struct ConfusableGroup {
skeleton : String
members : Array[String]
} derive(Debug)
///|
fn group_by_key(
names : Array[String],
key : (String) -> String,
) -> Array[ConfusableGroup] {
let buckets : Map[String, Array[String]] = Map([])
let order : Array[String] = []
for name in names {
let sk = key(name)
match buckets.get(sk) {
Some(group) => group.push(name)
None => {
let group : Array[String] = [name]
buckets.set(sk, group)
order.push(sk)
}
}
}
let results : Array[ConfusableGroup] = []
for sk in order {
if buckets.get(sk) is Some(members) && members.length() > 1 {
results.push({ skeleton: sk, members, })
}
}
results
}
///|
pub fn find_confusable_groups(names : Array[String]) -> Array[ConfusableGroup] {
group_by_key(names, @confusable.skeleton)
}
///|
pub fn find_confusable_groups_ignore_case(
names : Array[String],
) -> Array[ConfusableGroup] {
group_by_key(names, @confusable.skeleton_case_folded)
}