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}