d3rs/array/
sets.rs

1//! Set operations for arrays
2//!
3//! Provides functions for computing set differences, intersections, and unions.
4
5use std::collections::HashSet;
6use std::hash::Hash;
7
8/// Returns elements that are in `a` but not in `b`.
9///
10/// # Example
11///
12/// ```
13/// use d3rs::array::difference;
14///
15/// let a = vec![1, 2, 3, 4, 5];
16/// let b = vec![3, 4, 5, 6, 7];
17/// assert_eq!(difference(&a, &b), vec![1, 2]);
18/// ```
19pub fn difference<T: Clone + Eq + Hash>(a: &[T], b: &[T]) -> Vec<T> {
20    let b_set: HashSet<&T> = b.iter().collect();
21    a.iter().filter(|x| !b_set.contains(x)).cloned().collect()
22}
23
24/// Returns elements that are in both `a` and `b`.
25///
26/// # Example
27///
28/// ```
29/// use d3rs::array::intersection;
30///
31/// let a = vec![1, 2, 3, 4, 5];
32/// let b = vec![3, 4, 5, 6, 7];
33/// assert_eq!(intersection(&a, &b), vec![3, 4, 5]);
34/// ```
35pub fn intersection<T: Clone + Eq + Hash>(a: &[T], b: &[T]) -> Vec<T> {
36    let b_set: HashSet<&T> = b.iter().collect();
37    a.iter().filter(|x| b_set.contains(x)).cloned().collect()
38}
39
40/// Returns elements that are in either `a` or `b` (or both).
41///
42/// The order is: all elements from `a`, then elements from `b` not in `a`.
43///
44/// # Example
45///
46/// ```
47/// use d3rs::array::union;
48///
49/// let a = vec![1, 2, 3];
50/// let b = vec![3, 4, 5];
51/// assert_eq!(union(&a, &b), vec![1, 2, 3, 4, 5]);
52/// ```
53pub fn union<T: Clone + Eq + Hash>(a: &[T], b: &[T]) -> Vec<T> {
54    let mut seen: HashSet<T> = HashSet::new();
55    let mut result: Vec<T> = Vec::new();
56
57    for item in a {
58        if seen.insert(item.clone()) {
59            result.push(item.clone());
60        }
61    }
62
63    for item in b {
64        if seen.insert(item.clone()) {
65            result.push(item.clone());
66        }
67    }
68
69    result
70}
71
72/// Returns the symmetric difference: elements in `a` or `b` but not both.
73///
74/// # Example
75///
76/// ```
77/// use d3rs::array::symmetric_difference;
78///
79/// let a = vec![1, 2, 3, 4];
80/// let b = vec![3, 4, 5, 6];
81/// let result = symmetric_difference(&a, &b);
82/// assert!(result.contains(&1));
83/// assert!(result.contains(&2));
84/// assert!(result.contains(&5));
85/// assert!(result.contains(&6));
86/// assert!(!result.contains(&3));
87/// assert!(!result.contains(&4));
88/// ```
89pub fn symmetric_difference<T: Clone + Eq + Hash>(a: &[T], b: &[T]) -> Vec<T> {
90    let a_set: HashSet<&T> = a.iter().collect();
91    let b_set: HashSet<&T> = b.iter().collect();
92
93    let mut result: Vec<T> = Vec::new();
94
95    // Elements in a but not b
96    for item in a {
97        if !b_set.contains(item) {
98            result.push(item.clone());
99        }
100    }
101
102    // Elements in b but not a
103    for item in b {
104        if !a_set.contains(item) {
105            result.push(item.clone());
106        }
107    }
108
109    result
110}
111
112/// Returns true if `a` is a subset of `b` (all elements of `a` are in `b`).
113///
114/// # Example
115///
116/// ```
117/// use d3rs::array::is_subset;
118///
119/// let a = vec![1, 2, 3];
120/// let b = vec![1, 2, 3, 4, 5];
121/// assert!(is_subset(&a, &b));
122/// assert!(!is_subset(&b, &a));
123/// ```
124pub fn is_subset<T: Eq + Hash>(a: &[T], b: &[T]) -> bool {
125    let b_set: HashSet<&T> = b.iter().collect();
126    a.iter().all(|x| b_set.contains(x))
127}
128
129/// Returns true if `a` is a superset of `b` (all elements of `b` are in `a`).
130///
131/// # Example
132///
133/// ```
134/// use d3rs::array::is_superset;
135///
136/// let a = vec![1, 2, 3, 4, 5];
137/// let b = vec![1, 2, 3];
138/// assert!(is_superset(&a, &b));
139/// ```
140pub fn is_superset<T: Eq + Hash>(a: &[T], b: &[T]) -> bool {
141    is_subset(b, a)
142}
143
144/// Returns true if `a` and `b` have no elements in common.
145///
146/// # Example
147///
148/// ```
149/// use d3rs::array::is_disjoint;
150///
151/// let a = vec![1, 2, 3];
152/// let b = vec![4, 5, 6];
153/// let c = vec![3, 4, 5];
154/// assert!(is_disjoint(&a, &b));
155/// assert!(!is_disjoint(&a, &c));
156/// ```
157pub fn is_disjoint<T: Eq + Hash>(a: &[T], b: &[T]) -> bool {
158    let b_set: HashSet<&T> = b.iter().collect();
159    !a.iter().any(|x| b_set.contains(x))
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn test_difference() {
168        let a = vec![1, 2, 3, 4, 5];
169        let b = vec![3, 4, 5, 6, 7];
170        assert_eq!(difference(&a, &b), vec![1, 2]);
171    }
172
173    #[test]
174    fn test_intersection() {
175        let a = vec![1, 2, 3, 4, 5];
176        let b = vec![3, 4, 5, 6, 7];
177        assert_eq!(intersection(&a, &b), vec![3, 4, 5]);
178    }
179
180    #[test]
181    fn test_union() {
182        let a = vec![1, 2, 3];
183        let b = vec![3, 4, 5];
184        assert_eq!(union(&a, &b), vec![1, 2, 3, 4, 5]);
185    }
186
187    #[test]
188    fn test_symmetric_difference() {
189        let a = vec![1, 2, 3, 4];
190        let b = vec![3, 4, 5, 6];
191        let result = symmetric_difference(&a, &b);
192        assert_eq!(result.len(), 4);
193        assert!(result.contains(&1));
194        assert!(result.contains(&2));
195        assert!(result.contains(&5));
196        assert!(result.contains(&6));
197    }
198
199    #[test]
200    fn test_subset_superset() {
201        let a = vec![1, 2, 3];
202        let b = vec![1, 2, 3, 4, 5];
203        assert!(is_subset(&a, &b));
204        assert!(!is_subset(&b, &a));
205        assert!(is_superset(&b, &a));
206    }
207
208    #[test]
209    fn test_disjoint() {
210        let a = vec![1, 2, 3];
211        let b = vec![4, 5, 6];
212        let c = vec![3, 4, 5];
213        assert!(is_disjoint(&a, &b));
214        assert!(!is_disjoint(&a, &c));
215    }
216}