use std::collections::{HashMap, HashSet};
pub struct KAnonymity {
k: usize,
}
impl KAnonymity {
pub fn new(k: usize) -> Self {
Self { k }
}
pub fn check(&self, records: &[Vec<String>], quasi_identifiers: &[usize]) -> bool {
let mut groups: HashMap<Vec<String>, usize> = HashMap::new();
for record in records {
let qi: Vec<String> = quasi_identifiers
.iter()
.filter_map(|&i| record.get(i).cloned())
.collect();
*groups.entry(qi).or_insert(0) += 1;
}
groups.values().all(|&count| count >= self.k)
}
pub fn min_group_size(&self, records: &[Vec<String>], quasi_identifiers: &[usize]) -> usize {
let mut groups: HashMap<Vec<String>, usize> = HashMap::new();
for record in records {
let qi: Vec<String> = quasi_identifiers
.iter()
.filter_map(|&i| record.get(i).cloned())
.collect();
*groups.entry(qi).or_insert(0) += 1;
}
groups.values().copied().min().unwrap_or(0)
}
}
pub struct LDiversity {
l: usize,
}
impl LDiversity {
pub fn new(l: usize) -> Self {
Self { l }
}
pub fn check(
&self,
records: &[Vec<String>],
quasi_identifiers: &[usize],
sensitive_attr: usize,
) -> bool {
let mut groups: HashMap<Vec<String>, HashSet<String>> = HashMap::new();
for record in records {
let qi: Vec<String> = quasi_identifiers
.iter()
.filter_map(|&i| record.get(i).cloned())
.collect();
if let Some(sensitive) = record.get(sensitive_attr) {
groups.entry(qi).or_default().insert(sensitive.clone());
}
}
groups.values().all(|values| values.len() >= self.l)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_k_anonymity() {
let checker = KAnonymity::new(2);
let records = vec![
vec![
"Alice".to_string(),
"30".to_string(),
"Engineer".to_string(),
],
vec!["Bob".to_string(), "30".to_string(), "Doctor".to_string()],
vec![
"Charlie".to_string(),
"40".to_string(),
"Teacher".to_string(),
],
vec!["David".to_string(), "40".to_string(), "Lawyer".to_string()],
];
assert!(checker.check(&records, &[1]));
}
}