rust_lodash/collection/
query.rs

1/*!
2Query methods for Lodash-RS.
3
4This module provides query methods like `find`, `includes`, `every`, `some`, etc.
5These methods are used to search and test elements in collections.
6*/
7
8use crate::collection::Collection;
9// Note: These imports are kept for future use in error handling and type constraints
10// use crate::utils::{LodashError, Result, Predicate};
11
12/// Iterate over elements of collection, returning the first element
13/// the predicate returns truthy for.
14/// 
15/// # Examples
16/// 
17/// ```
18/// use rust_lodash::collection::query::find;
19/// 
20/// let numbers = vec![1, 2, 3, 4, 5];
21/// let first_even = find(&numbers, |x| x % 2 == 0);
22/// assert_eq!(first_even, Some(&2));
23/// 
24/// let not_found = find(&numbers, |x| *x > 10);
25/// assert_eq!(not_found, None);
26/// ```
27pub fn find<T, F>(collection: &[T], predicate: F) -> Option<&T>
28where
29    F: Fn(&T) -> bool,
30{
31    collection.iter().find(|item| predicate(item))
32}
33
34/// This method is like `find` except that it iterates over elements of
35/// collection from right to left.
36/// 
37/// # Examples
38/// 
39/// ```
40/// use rust_lodash::collection::query::find_last;
41/// 
42/// let numbers = vec![1, 2, 3, 4, 5];
43/// let last_even = find_last(&numbers, |x| x % 2 == 0);
44/// assert_eq!(last_even, Some(&4));
45/// ```
46pub fn find_last<T, F>(collection: &[T], predicate: F) -> Option<&T>
47where
48    F: Fn(&T) -> bool,
49{
50    collection.iter().rev().find(|item| predicate(item))
51}
52
53/// Check if value is in collection.
54/// 
55/// # Examples
56/// 
57/// ```
58/// use rust_lodash::collection::query::includes;
59/// 
60/// let numbers = vec![1, 2, 3, 4, 5];
61/// assert!(includes(&numbers, &3));
62/// assert!(!includes(&numbers, &6));
63/// ```
64pub fn includes<T>(collection: &[T], value: &T) -> bool
65where
66    T: PartialEq,
67{
68    collection.contains(value)
69}
70
71/// Check if predicate returns truthy for all elements of collection.
72/// 
73/// # Examples
74/// 
75/// ```
76/// use rust_lodash::collection::query::every;
77/// 
78/// let numbers = vec![2, 4, 6, 8];
79/// assert!(every(&numbers, |x| x % 2 == 0));
80/// 
81/// let mixed = vec![2, 4, 5, 8];
82/// assert!(!every(&mixed, |x| x % 2 == 0));
83/// ```
84pub fn every<T, F>(collection: &[T], predicate: F) -> bool
85where
86    F: Fn(&T) -> bool,
87{
88    collection.iter().all(predicate)
89}
90
91/// Check if predicate returns truthy for any element of collection.
92/// 
93/// # Examples
94/// 
95/// ```
96/// use rust_lodash::collection::query::some;
97/// 
98/// let numbers = vec![1, 2, 3, 4, 5];
99/// assert!(some(&numbers, |x| x % 2 == 0));
100/// 
101/// let odds = vec![1, 3, 5, 7];
102/// assert!(!some(&odds, |x| x % 2 == 0));
103/// ```
104pub fn some<T, F>(collection: &[T], predicate: F) -> bool
105where
106    F: Fn(&T) -> bool,
107{
108    collection.iter().any(predicate)
109}
110
111/// Create an object composed of keys generated from the results of running
112/// each element of collection through iteratee. The corresponding value of
113/// each key is the number of times the key was returned by iteratee.
114/// 
115/// # Examples
116/// 
117/// ```
118/// use rust_lodash::collection::query::count_by;
119/// use std::collections::HashMap;
120/// 
121/// let numbers = vec![6.1, 4.2, 6.3];
122/// let counts = count_by(&numbers, |x| (*x as f64).floor() as i32);
123/// assert_eq!(counts.get(&6), Some(&2));
124/// assert_eq!(counts.get(&4), Some(&1));
125/// ```
126pub fn count_by<T, K, F>(collection: &[T], iteratee: F) -> std::collections::HashMap<K, usize>
127where
128    K: std::hash::Hash + Eq,
129    F: Fn(&T) -> K,
130{
131    let mut counts = std::collections::HashMap::new();
132    for item in collection {
133        let key = iteratee(item);
134        *counts.entry(key).or_insert(0) += 1;
135    }
136    counts
137}
138
139/// Create an array of elements split into two groups, the first of which
140/// contains elements the predicate returns truthy for, while the second
141/// contains elements the predicate returns falsy for.
142/// 
143/// # Examples
144/// 
145/// ```
146/// use rust_lodash::collection::query::partition;
147/// 
148/// let numbers = vec![1, 2, 3, 4, 5];
149/// let (evens, odds) = partition(&numbers, |x| x % 2 == 0);
150/// assert_eq!(evens, vec![2, 4]);
151/// assert_eq!(odds, vec![1, 3, 5]);
152/// ```
153pub fn partition<T, F>(collection: &[T], predicate: F) -> (Vec<T>, Vec<T>)
154where
155    T: Clone,
156    F: Fn(&T) -> bool,
157{
158    let mut truthy = Vec::new();
159    let mut falsy = Vec::new();
160    
161    for item in collection {
162        if predicate(item) {
163            truthy.push(item.clone());
164        } else {
165            falsy.push(item.clone());
166        }
167    }
168    
169    (truthy, falsy)
170}
171
172/// Collection methods that work on the `Collection` type.
173impl<T> Collection<T> {
174    /// Iterate over elements, returning the first element
175    /// the predicate returns truthy for.
176    /// 
177    /// # Examples
178    /// 
179    /// ```
180    /// use rust_lodash::collection::Collection;
181    /// 
182    /// let collection = Collection::new(vec![1, 2, 3, 4, 5]);
183    /// let first_even = collection.find(|x| x % 2 == 0);
184    /// assert_eq!(first_even, Some(&2));
185    /// ```
186    pub fn find<F>(&self, predicate: F) -> Option<&T>
187    where
188        F: Fn(&T) -> bool,
189    {
190        find(&self.data, predicate)
191    }
192
193    /// This method is like `find` except that it iterates from right to left.
194    /// 
195    /// # Examples
196    /// 
197    /// ```
198    /// use rust_lodash::collection::Collection;
199    /// 
200    /// let collection = Collection::new(vec![1, 2, 3, 4, 5]);
201    /// let last_even = collection.find_last(|x| x % 2 == 0);
202    /// assert_eq!(last_even, Some(&4));
203    /// ```
204    pub fn find_last<F>(&self, predicate: F) -> Option<&T>
205    where
206        F: Fn(&T) -> bool,
207    {
208        find_last(&self.data, predicate)
209    }
210
211    /// Check if value is in the collection.
212    /// 
213    /// # Examples
214    /// 
215    /// ```
216    /// use rust_lodash::collection::Collection;
217    /// 
218    /// let collection = Collection::new(vec![1, 2, 3, 4, 5]);
219    /// assert!(collection.includes(&3));
220    /// assert!(!collection.includes(&6));
221    /// ```
222    pub fn includes(&self, value: &T) -> bool
223    where
224        T: PartialEq,
225    {
226        includes(&self.data, value)
227    }
228
229    /// Check if predicate returns truthy for all elements.
230    /// 
231    /// # Examples
232    /// 
233    /// ```
234    /// use rust_lodash::collection::Collection;
235    /// 
236    /// let collection = Collection::new(vec![2, 4, 6, 8]);
237    /// assert!(collection.every(|x| x % 2 == 0));
238    /// ```
239    pub fn every<F>(&self, predicate: F) -> bool
240    where
241        F: Fn(&T) -> bool,
242    {
243        every(&self.data, predicate)
244    }
245
246    /// Check if predicate returns truthy for any element.
247    /// 
248    /// # Examples
249    /// 
250    /// ```
251    /// use rust_lodash::collection::Collection;
252    /// 
253    /// let collection = Collection::new(vec![1, 2, 3, 4, 5]);
254    /// assert!(collection.some(|x| x % 2 == 0));
255    /// ```
256    pub fn some<F>(&self, predicate: F) -> bool
257    where
258        F: Fn(&T) -> bool,
259    {
260        some(&self.data, predicate)
261    }
262
263    /// Create an object composed of keys generated from the results of running
264    /// each element through iteratee.
265    /// 
266    /// # Examples
267    /// 
268    /// ```
269    /// use rust_lodash::collection::Collection;
270    /// use std::collections::HashMap;
271    /// 
272    /// let collection = Collection::new(vec![6.1, 4.2, 6.3]);
273    /// let counts = collection.count_by(|x| (*x as f64).floor() as i32);
274    /// assert_eq!(counts.get(&6), Some(&2));
275    /// ```
276    pub fn count_by<K, F>(&self, iteratee: F) -> std::collections::HashMap<K, usize>
277    where
278        K: std::hash::Hash + Eq,
279        F: Fn(&T) -> K,
280    {
281        count_by(&self.data, iteratee)
282    }
283
284    /// Create an array of elements split into two groups.
285    /// 
286    /// # Examples
287    /// 
288    /// ```
289    /// use rust_lodash::collection::Collection;
290    /// 
291    /// let collection = Collection::new(vec![1, 2, 3, 4, 5]);
292    /// let (evens, odds) = collection.partition(|x| x % 2 == 0);
293    /// assert_eq!(evens, vec![2, 4]);
294    /// assert_eq!(odds, vec![1, 3, 5]);
295    /// ```
296    pub fn partition<F>(&self, predicate: F) -> (Vec<T>, Vec<T>)
297    where
298        T: Clone,
299        F: Fn(&T) -> bool,
300    {
301        partition(&self.data, predicate)
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308    // use std::collections::HashMap; // For future use in advanced query operations
309
310    #[test]
311    fn test_find() {
312        let numbers = vec![1, 2, 3, 4, 5];
313        let first_even = find(&numbers, |x| x % 2 == 0);
314        assert_eq!(first_even, Some(&2));
315
316        let not_found = find(&numbers, |x| *x > 10);
317        assert_eq!(not_found, None);
318    }
319
320    #[test]
321    fn test_find_last() {
322        let numbers = vec![1, 2, 3, 4, 5];
323        let last_even = find_last(&numbers, |x| x % 2 == 0);
324        assert_eq!(last_even, Some(&4));
325    }
326
327    #[test]
328    fn test_includes() {
329        let numbers = vec![1, 2, 3, 4, 5];
330        assert!(includes(&numbers, &3));
331        assert!(!includes(&numbers, &6));
332    }
333
334    #[test]
335    fn test_every() {
336        let numbers = vec![2, 4, 6, 8];
337        assert!(every(&numbers, |x| x % 2 == 0));
338
339        let mixed = vec![2, 4, 5, 8];
340        assert!(!every(&mixed, |x| x % 2 == 0));
341    }
342
343    #[test]
344    fn test_some() {
345        let numbers = vec![1, 2, 3, 4, 5];
346        assert!(some(&numbers, |x| x % 2 == 0));
347
348        let odds = vec![1, 3, 5, 7];
349        assert!(!some(&odds, |x| x % 2 == 0));
350    }
351
352    #[test]
353    fn test_count_by() {
354        let numbers = vec![6.1, 4.2, 6.3];
355        let counts = count_by(&numbers, |x| {
356            #[allow(clippy::cast_possible_truncation, clippy::unnecessary_cast)]
357            {
358                (*x as f64).floor() as i32
359            }
360        });
361        assert_eq!(counts.get(&6), Some(&2));
362        assert_eq!(counts.get(&4), Some(&1));
363    }
364
365    #[test]
366    fn test_partition() {
367        let numbers = vec![1, 2, 3, 4, 5];
368        let (evens, odds) = partition(&numbers, |x| x % 2 == 0);
369        assert_eq!(evens, vec![2, 4]);
370        assert_eq!(odds, vec![1, 3, 5]);
371    }
372
373    #[test]
374    fn test_collection_find() {
375        let collection = Collection::new(vec![1, 2, 3, 4, 5]);
376        let first_even = collection.find(|x| x % 2 == 0);
377        assert_eq!(first_even, Some(&2));
378    }
379
380    #[test]
381    fn test_collection_find_last() {
382        let collection = Collection::new(vec![1, 2, 3, 4, 5]);
383        let last_even = collection.find_last(|x| x % 2 == 0);
384        assert_eq!(last_even, Some(&4));
385    }
386
387    #[test]
388    fn test_collection_includes() {
389        let collection = Collection::new(vec![1, 2, 3, 4, 5]);
390        assert!(collection.includes(&3));
391        assert!(!collection.includes(&6));
392    }
393
394    #[test]
395    fn test_collection_every() {
396        let collection = Collection::new(vec![2, 4, 6, 8]);
397        assert!(collection.every(|x| x % 2 == 0));
398    }
399
400    #[test]
401    fn test_collection_some() {
402        let collection = Collection::new(vec![1, 2, 3, 4, 5]);
403        assert!(collection.some(|x| x % 2 == 0));
404        
405        let odds_collection = Collection::new(vec![1, 3, 5, 7]);
406        assert!(!odds_collection.some(|x| x % 2 == 0));
407    }
408
409    #[test]
410    fn test_collection_count_by() {
411        let collection = Collection::new(vec![6.1, 4.2, 6.3]);
412        let counts = collection.count_by(|x| {
413            #[allow(clippy::cast_possible_truncation, clippy::unnecessary_cast)]
414            {
415                (*x as f64).floor() as i32
416            }
417        });
418        assert_eq!(counts.get(&6), Some(&2));
419    }
420
421    #[test]
422    fn test_collection_partition() {
423        let collection = Collection::new(vec![1, 2, 3, 4, 5]);
424        let (evens, odds) = collection.partition(|x| x % 2 == 0);
425        assert_eq!(evens, vec![2, 4]);
426        assert_eq!(odds, vec![1, 3, 5]);
427    }
428
429    #[test]
430    fn test_empty_collection() {
431        let empty: Vec<i32> = vec![];
432        assert_eq!(find(&empty, |x| x % 2 == 0), None);
433        assert!(every(&empty, |x| x % 2 == 0)); // vacuous truth
434        assert!(!some(&empty, |x| x % 2 == 0)); // vacuous false
435        assert!(!includes(&empty, &1));
436    }
437}