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}