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}