d3rs/array/
transform.rs

1//! Data transformation functions
2//!
3//! Provides functions for grouping, sorting, and transforming arrays.
4
5use std::collections::HashMap;
6use std::hash::Hash;
7
8/// Groups elements by a key function.
9///
10/// Returns a HashMap where each key maps to a Vec of elements with that key.
11///
12/// # Example
13///
14/// ```
15/// use d3rs::array::group;
16///
17/// let data = vec!["apple", "apricot", "banana", "blueberry", "cherry"];
18/// let grouped = group(&data, |s| s.chars().next().unwrap());
19///
20/// assert_eq!(grouped[&'a'].len(), 2);
21/// assert_eq!(grouped[&'b'].len(), 2);
22/// assert_eq!(grouped[&'c'].len(), 1);
23/// ```
24pub fn group<'a, T, K, F>(data: &'a [T], key: F) -> HashMap<K, Vec<&'a T>>
25where
26    K: Eq + Hash,
27    F: Fn(&T) -> K,
28{
29    let mut result: HashMap<K, Vec<&'a T>> = HashMap::new();
30    for item in data {
31        let k = key(item);
32        result.entry(k).or_default().push(item);
33    }
34    result
35}
36
37/// Groups elements and applies a reducer to each group.
38///
39/// # Example
40///
41/// ```
42/// use d3rs::array::rollup;
43///
44/// let data = vec![
45///     ("A", 10),
46///     ("B", 20),
47///     ("A", 30),
48///     ("B", 40),
49/// ];
50///
51/// let sums = rollup(&data, |item| item.0, |group| {
52///     group.iter().map(|item| item.1).sum::<i32>()
53/// });
54///
55/// assert_eq!(sums[&"A"], 40);
56/// assert_eq!(sums[&"B"], 60);
57/// ```
58pub fn rollup<T, K, V, F, R>(data: &[T], key: F, reduce: R) -> HashMap<K, V>
59where
60    K: Eq + Hash,
61    F: Fn(&T) -> K,
62    R: Fn(&[&T]) -> V,
63{
64    let groups = group(data, key);
65    groups.into_iter().map(|(k, v)| (k, reduce(&v))).collect()
66}
67
68/// Creates an index from key to element.
69///
70/// If multiple elements have the same key, the last one wins.
71///
72/// # Example
73///
74/// ```
75/// use d3rs::array::index;
76///
77/// let data = vec![
78///     ("id1", "Alice"),
79///     ("id2", "Bob"),
80///     ("id3", "Charlie"),
81/// ];
82///
83/// let indexed = index(&data, |item| item.0);
84/// assert_eq!(indexed[&"id2"].1, "Bob");
85/// ```
86pub fn index<'a, T, K, F>(data: &'a [T], key: F) -> HashMap<K, &'a T>
87where
88    K: Eq + Hash,
89    F: Fn(&T) -> K,
90{
91    let mut result: HashMap<K, &'a T> = HashMap::new();
92    for item in data {
93        let k = key(item);
94        result.insert(k, item);
95    }
96    result
97}
98
99/// Sorts a slice in place using a key accessor.
100///
101/// # Example
102///
103/// ```
104/// use d3rs::array::sort_by;
105///
106/// let mut data = vec![("c", 3), ("a", 1), ("b", 2)];
107/// sort_by(&mut data, |item| item.1);
108/// assert_eq!(data, vec![("a", 1), ("b", 2), ("c", 3)]);
109/// ```
110pub fn sort_by<T, K, F>(data: &mut [T], key: F)
111where
112    K: Ord,
113    F: Fn(&T) -> K,
114{
115    data.sort_by_key(|a| key(a));
116}
117
118/// Sorts a slice in place in descending order using a key accessor.
119///
120/// # Example
121///
122/// ```
123/// use d3rs::array::sort_by_desc;
124///
125/// let mut data = vec![("c", 3), ("a", 1), ("b", 2)];
126/// sort_by_desc(&mut data, |item| item.1);
127/// assert_eq!(data, vec![("c", 3), ("b", 2), ("a", 1)]);
128/// ```
129pub fn sort_by_desc<T, K, F>(data: &mut [T], key: F)
130where
131    K: Ord,
132    F: Fn(&T) -> K,
133{
134    data.sort_by(|a, b| key(b).cmp(&key(a)));
135}
136
137/// Randomly shuffles a slice using the Fisher-Yates algorithm.
138///
139/// # Example
140///
141/// ```
142/// use d3rs::array::shuffle;
143///
144/// let mut data = vec![1, 2, 3, 4, 5];
145/// shuffle(&mut data);
146/// // data is now shuffled
147/// ```
148pub fn shuffle<T>(data: &mut [T]) {
149    use std::time::{SystemTime, UNIX_EPOCH};
150
151    // Simple LCG random number generator
152    let mut seed = SystemTime::now()
153        .duration_since(UNIX_EPOCH)
154        .unwrap()
155        .as_nanos() as u64;
156
157    for i in (1..data.len()).rev() {
158        // Generate random index in [0, i]
159        seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1);
160        let j = ((seed >> 33) as usize) % (i + 1);
161        data.swap(i, j);
162    }
163}
164
165/// Randomly shuffles a slice using a seeded random number generator.
166///
167/// # Example
168///
169/// ```
170/// use d3rs::array::shuffle_seeded;
171///
172/// let mut data1 = vec![1, 2, 3, 4, 5];
173/// let mut data2 = vec![1, 2, 3, 4, 5];
174///
175/// shuffle_seeded(&mut data1, 12345);
176/// shuffle_seeded(&mut data2, 12345);
177///
178/// assert_eq!(data1, data2); // Same seed produces same shuffle
179/// ```
180pub fn shuffle_seeded<T>(data: &mut [T], seed: u64) {
181    let mut rng_seed = seed;
182
183    for i in (1..data.len()).rev() {
184        rng_seed = rng_seed.wrapping_mul(6364136223846793005).wrapping_add(1);
185        let j = ((rng_seed >> 33) as usize) % (i + 1);
186        data.swap(i, j);
187    }
188}
189
190/// Reverses the order of elements in a slice.
191///
192/// # Example
193///
194/// ```
195/// use d3rs::array::reverse;
196///
197/// let mut data = vec![1, 2, 3, 4, 5];
198/// reverse(&mut data);
199/// assert_eq!(data, vec![5, 4, 3, 2, 1]);
200/// ```
201pub fn reverse<T>(data: &mut [T]) {
202    data.reverse();
203}
204
205/// Returns a new Vec containing only unique elements (first occurrence).
206///
207/// # Example
208///
209/// ```
210/// use d3rs::array::unique;
211///
212/// let data = vec![1, 2, 2, 3, 1, 4, 3];
213/// assert_eq!(unique(&data), vec![1, 2, 3, 4]);
214/// ```
215pub fn unique<T: Clone + Eq + Hash>(data: &[T]) -> Vec<T> {
216    let mut seen = std::collections::HashSet::new();
217    let mut result = Vec::new();
218    for item in data {
219        if seen.insert(item.clone()) {
220            result.push(item.clone());
221        }
222    }
223    result
224}
225
226/// Returns pairs of consecutive elements.
227///
228/// # Example
229///
230/// ```
231/// use d3rs::array::pairs;
232///
233/// let data = vec![1, 2, 3, 4, 5];
234/// let result = pairs(&data);
235/// assert_eq!(result, vec![(&1, &2), (&2, &3), (&3, &4), (&4, &5)]);
236/// ```
237pub fn pairs<T>(data: &[T]) -> Vec<(&T, &T)> {
238    data.windows(2).map(|w| (&w[0], &w[1])).collect()
239}
240
241/// Returns cross product of two slices.
242///
243/// # Example
244///
245/// ```
246/// use d3rs::array::cross;
247///
248/// let a = vec![1, 2];
249/// let b = vec!["a", "b"];
250/// let result = cross(&a, &b);
251/// assert_eq!(result, vec![(&1, &"a"), (&1, &"b"), (&2, &"a"), (&2, &"b")]);
252/// ```
253pub fn cross<'a, 'b, T, U>(a: &'a [T], b: &'b [U]) -> Vec<(&'a T, &'b U)> {
254    let mut result = Vec::with_capacity(a.len() * b.len());
255    for item_a in a {
256        for item_b in b {
257            result.push((item_a, item_b));
258        }
259    }
260    result
261}
262
263/// Merges multiple sorted slices into a single sorted Vec.
264///
265/// # Example
266///
267/// ```
268/// use d3rs::array::merge_sorted;
269///
270/// let a = vec![1, 3, 5];
271/// let b = vec![2, 4, 6];
272/// assert_eq!(merge_sorted(&[&a[..], &b[..]]), vec![1, 2, 3, 4, 5, 6]);
273/// ```
274pub fn merge_sorted<T: Ord + Clone>(slices: &[&[T]]) -> Vec<T> {
275    let total_len: usize = slices.iter().map(|s| s.len()).sum();
276    let mut result = Vec::with_capacity(total_len);
277
278    // Simple k-way merge using indices
279    let mut indices: Vec<usize> = vec![0; slices.len()];
280
281    loop {
282        // Find the minimum element among all non-exhausted slices
283        let mut min_val: Option<&T> = None;
284        let mut min_idx = 0;
285
286        for (i, slice) in slices.iter().enumerate() {
287            if indices[i] < slice.len() {
288                let val = &slice[indices[i]];
289                if min_val.is_none() || val < min_val.unwrap() {
290                    min_val = Some(val);
291                    min_idx = i;
292                }
293            }
294        }
295
296        match min_val {
297            Some(val) => {
298                result.push(val.clone());
299                indices[min_idx] += 1;
300            }
301            None => break,
302        }
303    }
304
305    result
306}
307
308/// Filters elements by a predicate.
309///
310/// # Example
311///
312/// ```
313/// use d3rs::array::filter;
314///
315/// let data = vec![1, 2, 3, 4, 5, 6];
316/// let evens = filter(&data, |x| x % 2 == 0);
317/// assert_eq!(evens, vec![&2, &4, &6]);
318/// ```
319pub fn filter<T, F>(data: &[T], predicate: F) -> Vec<&T>
320where
321    F: Fn(&T) -> bool,
322{
323    data.iter().filter(|x| predicate(x)).collect()
324}
325
326/// Maps elements using a function.
327///
328/// # Example
329///
330/// ```
331/// use d3rs::array::map;
332///
333/// let data = vec![1, 2, 3, 4, 5];
334/// let squares = map(&data, |x| x * x);
335/// assert_eq!(squares, vec![1, 4, 9, 16, 25]);
336/// ```
337pub fn map<T, U, F>(data: &[T], f: F) -> Vec<U>
338where
339    F: Fn(&T) -> U,
340{
341    data.iter().map(f).collect()
342}
343
344/// Reduces elements to a single value.
345///
346/// # Example
347///
348/// ```
349/// use d3rs::array::reduce;
350///
351/// let data = vec![1, 2, 3, 4, 5];
352/// let sum = reduce(&data, 0, |acc, x| acc + x);
353/// assert_eq!(sum, 15);
354/// ```
355pub fn reduce<T, U, F>(data: &[T], initial: U, f: F) -> U
356where
357    F: Fn(U, &T) -> U,
358{
359    data.iter().fold(initial, f)
360}
361
362#[cfg(test)]
363mod tests {
364    use super::*;
365
366    #[test]
367    fn test_group() {
368        let data = vec!["apple", "apricot", "banana", "blueberry", "cherry"];
369        let grouped = group(&data, |s| s.chars().next().unwrap());
370
371        assert_eq!(grouped[&'a'].len(), 2);
372        assert_eq!(grouped[&'b'].len(), 2);
373        assert_eq!(grouped[&'c'].len(), 1);
374    }
375
376    #[test]
377    fn test_rollup() {
378        let data = vec![("A", 10), ("B", 20), ("A", 30), ("B", 40)];
379
380        let sums = rollup(
381            &data,
382            |item| item.0,
383            |group| group.iter().map(|item| item.1).sum::<i32>(),
384        );
385
386        assert_eq!(sums[&"A"], 40);
387        assert_eq!(sums[&"B"], 60);
388    }
389
390    #[test]
391    fn test_sort_by() {
392        let mut data = vec![("c", 3), ("a", 1), ("b", 2)];
393        sort_by(&mut data, |item| item.1);
394        assert_eq!(data, vec![("a", 1), ("b", 2), ("c", 3)]);
395    }
396
397    #[test]
398    fn test_shuffle_seeded() {
399        let mut data1 = vec![1, 2, 3, 4, 5];
400        let mut data2 = vec![1, 2, 3, 4, 5];
401
402        shuffle_seeded(&mut data1, 12345);
403        shuffle_seeded(&mut data2, 12345);
404
405        assert_eq!(data1, data2);
406    }
407
408    #[test]
409    fn test_unique() {
410        let data = vec![1, 2, 2, 3, 1, 4, 3];
411        assert_eq!(unique(&data), vec![1, 2, 3, 4]);
412    }
413
414    #[test]
415    fn test_pairs() {
416        let data = vec![1, 2, 3, 4, 5];
417        let result = pairs(&data);
418        assert_eq!(result, vec![(&1, &2), (&2, &3), (&3, &4), (&4, &5)]);
419    }
420
421    #[test]
422    fn test_cross() {
423        let a = vec![1, 2];
424        let b = vec!["a", "b"];
425        let result = cross(&a, &b);
426        assert_eq!(result, vec![(&1, &"a"), (&1, &"b"), (&2, &"a"), (&2, &"b")]);
427    }
428
429    #[test]
430    fn test_merge_sorted() {
431        let a = vec![1, 3, 5];
432        let b = vec![2, 4, 6];
433        assert_eq!(merge_sorted(&[&a[..], &b[..]]), vec![1, 2, 3, 4, 5, 6]);
434    }
435}