Skip to main content

spo_rhai/packages/
array_basic.rs

1#![cfg(not(feature = "no_index"))]
2
3use crate::api::deprecated::deprecated_array_functions;
4use crate::engine::OP_EQUALS;
5use crate::eval::{calc_index, calc_offset_len};
6use crate::module::ModuleFlags;
7use crate::plugin::*;
8use crate::{
9    def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext,
10    Position, RhaiResultOf, ERR, INT, MAX_USIZE_INT,
11};
12#[cfg(feature = "no_std")]
13use std::prelude::v1::*;
14use std::{any::TypeId, cmp::Ordering, mem};
15
16def_package! {
17    /// Package of basic array utilities.
18    pub BasicArrayPackage(lib) {
19        lib.flags |= ModuleFlags::STANDARD_LIB;
20
21        combine_with_exported_module!(lib, "array", array_functions);
22        combine_with_exported_module!(lib, "deprecated_array", deprecated_array_functions);
23
24        // Register array iterator
25        lib.set_iterable::<Array>();
26    }
27}
28
29#[export_module]
30pub mod array_functions {
31    /// Number of elements in the array.
32    #[rhai_fn(name = "len", get = "len", pure)]
33    pub fn len(array: &mut Array) -> INT {
34        array.len() as INT
35    }
36    /// Return true if the array is empty.
37    #[rhai_fn(name = "is_empty", get = "is_empty", pure)]
38    pub fn is_empty(array: &mut Array) -> bool {
39        array.len() == 0
40    }
41    /// Get a copy of the element at the `index` position in the array.
42    ///
43    /// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
44    /// * If `index` < -length of array, `()` is returned.
45    /// * If `index` ≥ length of array, `()` is returned.
46    ///
47    /// # Example
48    ///
49    /// ```rhai
50    /// let x = [1, 2, 3];
51    ///
52    /// print(x.get(0));        // prints 1
53    ///
54    /// print(x.get(-1));       // prints 3
55    ///
56    /// print(x.get(99));       // prints empty (for '()')
57    /// ```
58    pub fn get(array: &mut Array, index: INT) -> Dynamic {
59        if array.is_empty() {
60            return Dynamic::UNIT;
61        }
62
63        let (index, ..) = calc_offset_len(array.len(), index, 0);
64
65        if index >= array.len() {
66            return Dynamic::UNIT;
67        }
68
69        array[index].clone()
70    }
71    /// Set the element at the `index` position in the array to a new `value`.
72    ///
73    /// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
74    /// * If `index` < -length of array, the array is not modified.
75    /// * If `index` ≥ length of array, the array is not modified.
76    ///
77    /// # Example
78    ///
79    /// ```rhai
80    /// let x = [1, 2, 3];
81    ///
82    /// x.set(0, 42);
83    ///
84    /// print(x);           // prints "[42, 2, 3]"
85    ///
86    /// x.set(-3, 0);
87    ///
88    /// print(x);           // prints "[0, 2, 3]"
89    ///
90    /// x.set(99, 123);
91    ///
92    /// print(x);           // prints "[0, 2, 3]"
93    /// ```
94    pub fn set(array: &mut Array, index: INT, value: Dynamic) {
95        if array.is_empty() {
96            return;
97        }
98
99        let (index, ..) = calc_offset_len(array.len(), index, 0);
100
101        if index >= array.len() {
102            return;
103        }
104
105        array[index] = value;
106    }
107    /// Add a new element, which is not another array, to the end of the array.
108    ///
109    /// If `item` is `Array`, then `append` is more specific and will be called instead.
110    ///
111    /// # Example
112    ///
113    /// ```rhai
114    /// let x = [1, 2, 3];
115    ///
116    /// x.push("hello");
117    ///
118    /// print(x);       // prints [1, 2, 3, "hello"]
119    /// ```
120    pub fn push(array: &mut Array, item: Dynamic) {
121        array.push(item);
122    }
123    /// Add all the elements of another array to the end of the array.
124    ///
125    /// # Example
126    ///
127    /// ```rhai
128    /// let x = [1, 2, 3];
129    /// let y = [true, 'x'];
130    ///
131    /// x.append(y);
132    ///
133    /// print(x);       // prints "[1, 2, 3, true, 'x']"
134    /// ```
135    pub fn append(array: &mut Array, new_array: Array) {
136        if new_array.is_empty() {
137            return;
138        }
139
140        if array.is_empty() {
141            *array = new_array;
142        } else {
143            array.extend(new_array);
144        }
145    }
146    /// Combine two arrays into a new array and return it.
147    ///
148    /// # Example
149    ///
150    /// ```rhai
151    /// let x = [1, 2, 3];
152    /// let y = [true, 'x'];
153    ///
154    /// print(x + y);   // prints "[1, 2, 3, true, 'x']"
155    ///
156    /// print(x);       // prints "[1, 2, 3"
157    /// ```
158    #[rhai_fn(name = "+")]
159    pub fn concat(array1: Array, array2: Array) -> Array {
160        if array2.is_empty() {
161            return array1;
162        }
163
164        if array1.is_empty() {
165            return array2;
166        }
167
168        let mut array = array1;
169        array.extend(array2);
170        array
171    }
172    /// Add a new element into the array at a particular `index` position.
173    ///
174    /// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
175    /// * If `index` < -length of array, the element is added to the beginning of the array.
176    /// * If `index` ≥ length of array, the element is appended to the end of the array.
177    ///
178    /// # Example
179    ///
180    /// ```rhai
181    /// let x = [1, 2, 3];
182    ///
183    /// x.insert(0, "hello");
184    ///
185    /// x.insert(2, true);
186    ///
187    /// x.insert(-2, 42);
188    ///
189    /// print(x);       // prints ["hello", 1, true, 2, 42, 3]
190    /// ```
191    pub fn insert(array: &mut Array, index: INT, item: Dynamic) {
192        if array.is_empty() {
193            array.push(item);
194            return;
195        }
196
197        let (index, ..) = calc_offset_len(array.len(), index, 0);
198
199        if index >= array.len() {
200            array.push(item);
201        } else {
202            array.insert(index, item);
203        }
204    }
205    /// Pad the array to at least the specified length with copies of a specified element.
206    ///
207    /// If `len` ≤ length of array, no padding is done.
208    ///
209    /// # Example
210    ///
211    /// ```rhai
212    /// let x = [1, 2, 3];
213    ///
214    /// x.pad(5, 42);
215    ///
216    /// print(x);       // prints "[1, 2, 3, 42, 42]"
217    ///
218    /// x.pad(3, 123);
219    ///
220    /// print(x);       // prints "[1, 2, 3, 42, 42]"
221    /// ```
222    #[rhai_fn(return_raw)]
223    pub fn pad(
224        ctx: NativeCallContext,
225        array: &mut Array,
226        len: INT,
227        item: Dynamic,
228    ) -> RhaiResultOf<()> {
229        if len <= 0 {
230            return Ok(());
231        }
232
233        #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
234        let len = len.min(MAX_USIZE_INT) as usize;
235
236        if len <= array.len() {
237            return Ok(());
238        }
239
240        let _ctx = ctx;
241
242        // Check if array will be over max size limit
243        #[cfg(not(feature = "unchecked"))]
244        if _ctx.engine().max_array_size() > 0 {
245            let pad = len - array.len();
246            let (a, m, s) = crate::eval::calc_array_sizes(array);
247            let (ax, mx, sx) = crate::eval::calc_data_sizes(&item, true);
248
249            _ctx.engine()
250                .throw_on_size((a + pad + ax * pad, m + mx * pad, s + sx * pad))?;
251        }
252
253        array.resize(len, item);
254
255        Ok(())
256    }
257    /// Remove the last element from the array and return it.
258    ///
259    /// If the array is empty, `()` is returned.
260    ///
261    /// # Example
262    ///
263    /// ```rhai
264    /// let x = [1, 2, 3];
265    ///
266    /// print(x.pop());     // prints 3
267    ///
268    /// print(x);           // prints "[1, 2]"
269    /// ```
270    pub fn pop(array: &mut Array) -> Dynamic {
271        if array.is_empty() {
272            return Dynamic::UNIT;
273        }
274
275        array.pop().unwrap_or(Dynamic::UNIT)
276    }
277    /// Remove the first element from the array and return it.
278    ///
279    /// If the array is empty, `()` is returned.
280    ///
281    /// # Example
282    ///
283    /// ```rhai
284    /// let x = [1, 2, 3];
285    ///
286    /// print(x.shift());   // prints 1
287    ///
288    /// print(x);           // prints "[2, 3]"
289    /// ```
290    pub fn shift(array: &mut Array) -> Dynamic {
291        if array.is_empty() {
292            return Dynamic::UNIT;
293        }
294
295        array.remove(0)
296    }
297    /// Remove the element at the specified `index` from the array and return it.
298    ///
299    /// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
300    /// * If `index` < -length of array, `()` is returned.
301    /// * If `index` ≥ length of array, `()` is returned.
302    ///
303    /// # Example
304    ///
305    /// ```rhai
306    /// let x = [1, 2, 3];
307    ///
308    /// print(x.remove(1));     // prints 2
309    ///
310    /// print(x);               // prints "[1, 3]"
311    ///
312    /// print(x.remove(-2));    // prints 1
313    ///
314    /// print(x);               // prints "[3]"
315    /// ```
316    pub fn remove(array: &mut Array, index: INT) -> Dynamic {
317        let Ok(index) = calc_index(array.len(), index, true, || Err(())) else {
318            return Dynamic::UNIT;
319        };
320
321        array.remove(index)
322    }
323    /// Clear the array.
324    pub fn clear(array: &mut Array) {
325        if array.is_empty() {
326            return;
327        }
328
329        array.clear();
330    }
331    /// Cut off the array at the specified length.
332    ///
333    /// * If `len` ≤ 0, the array is cleared.
334    /// * If `len` ≥ length of array, the array is not truncated.
335    ///
336    /// # Example
337    ///
338    /// ```rhai
339    /// let x = [1, 2, 3, 4, 5];
340    ///
341    /// x.truncate(3);
342    ///
343    /// print(x);       // prints "[1, 2, 3]"
344    ///
345    /// x.truncate(10);
346    ///
347    /// print(x);       // prints "[1, 2, 3]"
348    /// ```
349    pub fn truncate(array: &mut Array, len: INT) {
350        if len <= 0 {
351            array.clear();
352            return;
353        }
354        if array.is_empty() {
355            return;
356        }
357
358        #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
359        let len = len.min(MAX_USIZE_INT) as usize;
360
361        if len > 0 {
362            array.truncate(len);
363        } else {
364            array.clear();
365        }
366    }
367    /// Cut off the head of the array, leaving a tail of the specified length.
368    ///
369    /// * If `len` ≤ 0, the array is cleared.
370    /// * If `len` ≥ length of array, the array is not modified.
371    ///
372    /// # Example
373    ///
374    /// ```rhai
375    /// let x = [1, 2, 3, 4, 5];
376    ///
377    /// x.chop(3);
378    ///
379    /// print(x);       // prints "[3, 4, 5]"
380    ///
381    /// x.chop(10);
382    ///
383    /// print(x);       // prints "[3, 4, 5]"
384    /// ```
385    pub fn chop(array: &mut Array, len: INT) {
386        if len <= 0 {
387            array.clear();
388            return;
389        }
390        if array.is_empty() {
391            return;
392        }
393
394        #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
395        let len = len.min(MAX_USIZE_INT) as usize;
396
397        if len == 0 {
398            array.clear();
399        } else if len < array.len() {
400            array.drain(0..array.len() - len);
401        }
402    }
403    /// Reverse all the elements in the array.
404    ///
405    /// # Example
406    ///
407    /// ```rhai
408    /// let x = [1, 2, 3, 4, 5];
409    ///
410    /// x.reverse();
411    ///
412    /// print(x);       // prints "[5, 4, 3, 2, 1]"
413    /// ```
414    pub fn reverse(array: &mut Array) {
415        if array.is_empty() {
416            return;
417        }
418
419        array.reverse();
420    }
421    /// Replace an exclusive range of the array with another array.
422    ///
423    /// # Example
424    ///
425    /// ```rhai
426    /// let x = [1, 2, 3, 4, 5];
427    /// let y = [7, 8, 9, 10];
428    ///
429    /// x.splice(1..3, y);
430    ///
431    /// print(x);       // prints "[1, 7, 8, 9, 10, 4, 5]"
432    /// ```
433    #[rhai_fn(name = "splice")]
434    pub fn splice_range(array: &mut Array, range: ExclusiveRange, replace: Array) {
435        let start = INT::max(range.start, 0);
436        let end = INT::max(range.end, start);
437        splice(array, start, end - start, replace);
438    }
439    /// Replace an inclusive range of the array with another array.
440    ///
441    /// # Example
442    ///
443    /// ```rhai
444    /// let x = [1, 2, 3, 4, 5];
445    /// let y = [7, 8, 9, 10];
446    ///
447    /// x.splice(1..=3, y);
448    ///
449    /// print(x);       // prints "[1, 7, 8, 9, 10, 5]"
450    /// ```
451    #[rhai_fn(name = "splice")]
452    pub fn splice_inclusive_range(array: &mut Array, range: InclusiveRange, replace: Array) {
453        let start = INT::max(*range.start(), 0);
454        let end = INT::max(*range.end(), start);
455        splice(array, start, end - start + 1, replace);
456    }
457    /// Replace a portion of the array with another array.
458    ///
459    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
460    /// * If `start` < -length of array, position counts from the beginning of the array.
461    /// * If `start` ≥ length of array, the other array is appended to the end of the array.
462    /// * If `len` ≤ 0, the other array is inserted into the array at the `start` position without replacing any element.
463    /// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is replaced.
464    ///
465    /// # Example
466    ///
467    /// ```rhai
468    /// let x = [1, 2, 3, 4, 5];
469    /// let y = [7, 8, 9, 10];
470    ///
471    /// x.splice(1, 2, y);
472    ///
473    /// print(x);       // prints "[1, 7, 8, 9, 10, 4, 5]"
474    ///
475    /// x.splice(-5, 4, y);
476    ///
477    /// print(x);       // prints "[1, 7, 7, 8, 9, 10, 5]"
478    /// ```
479    pub fn splice(array: &mut Array, start: INT, len: INT, replace: Array) {
480        if array.is_empty() {
481            *array = replace;
482            return;
483        }
484
485        let (start, len) = calc_offset_len(array.len(), start, len);
486
487        if start >= array.len() {
488            array.extend(replace);
489        } else {
490            array.splice(start..start + len, replace);
491        }
492    }
493    /// Copy an exclusive range of the array and return it as a new array.
494    ///
495    /// # Example
496    ///
497    /// ```rhai
498    /// let x = [1, 2, 3, 4, 5];
499    ///
500    /// print(x.extract(1..3));     // prints "[2, 3]"
501    ///
502    /// print(x);                   // prints "[1, 2, 3, 4, 5]"
503    /// ```
504    #[rhai_fn(name = "extract")]
505    pub fn extract_range(array: &mut Array, range: ExclusiveRange) -> Array {
506        let start = INT::max(range.start, 0);
507        let end = INT::max(range.end, start);
508        extract(array, start, end - start)
509    }
510    /// Copy an inclusive range of the array and return it as a new array.
511    ///
512    /// # Example
513    ///
514    /// ```rhai
515    /// let x = [1, 2, 3, 4, 5];
516    ///
517    /// print(x.extract(1..=3));    // prints "[2, 3, 4]"
518    ///
519    /// print(x);                   // prints "[1, 2, 3, 4, 5]"
520    /// ```
521    #[rhai_fn(name = "extract")]
522    pub fn extract_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
523        let start = INT::max(*range.start(), 0);
524        let end = INT::max(*range.end(), start);
525        extract(array, start, end - start + 1)
526    }
527    /// Copy a portion of the array and return it as a new array.
528    ///
529    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
530    /// * If `start` < -length of array, position counts from the beginning of the array.
531    /// * If `start` ≥ length of array, an empty array is returned.
532    /// * If `len` ≤ 0, an empty array is returned.
533    /// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is copied and returned.
534    ///
535    /// # Example
536    ///
537    /// ```rhai
538    /// let x = [1, 2, 3, 4, 5];
539    ///
540    /// print(x.extract(1, 3));     // prints "[2, 3, 4]"
541    ///
542    /// print(x.extract(-3, 2));    // prints "[3, 4]"
543    ///
544    /// print(x);                   // prints "[1, 2, 3, 4, 5]"
545    /// ```
546    pub fn extract(array: &mut Array, start: INT, len: INT) -> Array {
547        if array.is_empty() || len <= 0 {
548            return Array::new();
549        }
550
551        let (start, len) = calc_offset_len(array.len(), start, len);
552
553        if len == 0 {
554            return Array::new();
555        }
556
557        array[start..start + len].to_vec()
558    }
559    /// Copy a portion of the array beginning at the `start` position till the end and return it as
560    /// a new array.
561    ///
562    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
563    /// * If `start` < -length of array, the entire array is copied and returned.
564    /// * If `start` ≥ length of array, an empty array is returned.
565    ///
566    /// # Example
567    ///
568    /// ```rhai
569    /// let x = [1, 2, 3, 4, 5];
570    ///
571    /// print(x.extract(2));        // prints "[3, 4, 5]"
572    ///
573    /// print(x.extract(-3));       // prints "[3, 4, 5]"
574    ///
575    /// print(x);                   // prints "[1, 2, 3, 4, 5]"
576    /// ```
577    #[rhai_fn(name = "extract")]
578    pub fn extract_tail(array: &mut Array, start: INT) -> Array {
579        extract(array, start, INT::MAX)
580    }
581    /// Cut off the array at `index` and return it as a new array.
582    ///
583    /// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
584    /// * If `index` is zero, the entire array is cut and returned.
585    /// * If `index` < -length of array, the entire array is cut and returned.
586    /// * If `index` ≥ length of array, nothing is cut from the array and an empty array is returned.
587    ///
588    /// # Example
589    ///
590    /// ```rhai
591    /// let x = [1, 2, 3, 4, 5];
592    ///
593    /// let y = x.split(2);
594    ///
595    /// print(y);           // prints "[3, 4, 5]"
596    ///
597    /// print(x);           // prints "[1, 2]"
598    /// ```
599    #[rhai_fn(name = "split")]
600    pub fn split_at(array: &mut Array, index: INT) -> Array {
601        if array.is_empty() {
602            return Array::new();
603        }
604
605        let (start, len) = calc_offset_len(array.len(), index, INT::MAX);
606
607        if start >= array.len() {
608            return Array::new();
609        }
610
611        if start == 0 {
612            if len >= array.len() {
613                return mem::take(array);
614            }
615
616            let mut result = Array::new();
617            result.extend(array.drain(array.len() - len..));
618            return result;
619        }
620
621        let mut result = Array::new();
622        result.extend(array.drain(start..));
623        result
624    }
625
626    /// Iterate through all the elements in the array, applying a `process` function to each element in turn.
627    /// Each element is bound to `this` before calling the function.
628    ///
629    /// # Function Parameters
630    ///
631    /// * `this`: bound to array element (mutable)
632    /// * `index` _(optional)_: current index in the array
633    ///
634    /// # Example
635    ///
636    /// ```rhai
637    /// let x = [1, 2, 3, 4, 5];
638    ///
639    /// x.for_each(|| this *= this);
640    ///
641    /// print(x);       // prints "[1, 4, 9, 16, 25]"
642    ///
643    /// x.for_each(|i| this *= i);
644    ///
645    /// print(x);       // prints "[0, 2, 6, 12, 20]"
646    /// ```
647    #[rhai_fn(return_raw)]
648    pub fn for_each(ctx: NativeCallContext, array: &mut Array, map: FnPtr) -> RhaiResultOf<()> {
649        if array.is_empty() {
650            return Ok(());
651        }
652
653        for (i, item) in array.iter_mut().enumerate() {
654            let ex = [(i as INT).into()];
655
656            let _ = map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex, None)?;
657        }
658
659        Ok(())
660    }
661
662    /// Iterate through all the elements in the array, applying a `mapper` function to each element
663    /// in turn, and return the results as a new array.
664    ///
665    /// # No Function Parameter
666    ///
667    /// Array element (mutable) is bound to `this`.
668    ///
669    /// This method is marked _pure_; the `mapper` function should not mutate array elements.
670    ///
671    /// # Function Parameters
672    ///
673    /// * `element`: copy of array element
674    /// * `index` _(optional)_: current index in the array
675    ///
676    /// # Example
677    ///
678    /// ```rhai
679    /// let x = [1, 2, 3, 4, 5];
680    ///
681    /// let y = x.map(|v| v * v);
682    ///
683    /// print(y);       // prints "[1, 4, 9, 16, 25]"
684    ///
685    /// let y = x.map(|v, i| v * i);
686    ///
687    /// print(y);       // prints "[0, 2, 6, 12, 20]"
688    /// ```
689    #[rhai_fn(return_raw, pure)]
690    pub fn map(ctx: NativeCallContext, array: &mut Array, map: FnPtr) -> RhaiResultOf<Array> {
691        if array.is_empty() {
692            return Ok(Array::new());
693        }
694
695        let mut ar = Array::with_capacity(array.len());
696
697        for (i, item) in array.iter_mut().enumerate() {
698            let ex = [(i as INT).into()];
699            ar.push(map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex, Some(0))?);
700        }
701
702        Ok(ar)
703    }
704
705    /// Iterate through all the elements in the array, applying a `filter` function to each element
706    /// in turn, and return a copy of all elements (in order) that return `true` as a new array.
707    ///
708    /// # No Function Parameter
709    ///
710    /// Array element (mutable) is bound to `this`.
711    ///
712    /// This method is marked _pure_; the `filter` function should not mutate array elements.
713    ///
714    /// # Function Parameters
715    ///
716    /// * `element`: copy of array element
717    /// * `index` _(optional)_: current index in the array
718    ///
719    /// # Example
720    ///
721    /// ```rhai
722    /// let x = [1, 2, 3, 4, 5];
723    ///
724    /// let y = x.filter(|v| v >= 3);
725    ///
726    /// print(y);       // prints "[3, 4, 5]"
727    ///
728    /// let y = x.filter(|v, i| v * i >= 10);
729    ///
730    /// print(y);       // prints "[12, 20]"
731    /// ```
732    #[rhai_fn(return_raw, pure)]
733    pub fn filter(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
734        if array.is_empty() {
735            return Ok(Array::new());
736        }
737
738        let mut ar = Array::new();
739
740        for (i, item) in array.iter_mut().enumerate() {
741            let ex = [(i as INT).into()];
742
743            if filter
744                .call_raw_with_extra_args("filter", &ctx, Some(item), [], ex, Some(0))?
745                .as_bool()
746                .unwrap_or(false)
747            {
748                ar.push(item.clone());
749            }
750        }
751
752        Ok(ar)
753    }
754    /// Return `true` if the array contains an element that equals `value`.
755    ///
756    /// The operator `==` is used to compare elements with `value` and must be defined,
757    /// otherwise `false` is assumed.
758    ///
759    /// This function also drives the `in` operator.
760    ///
761    /// # Example
762    ///
763    /// ```rhai
764    /// let x = [1, 2, 3, 4, 5];
765    ///
766    /// // The 'in' operator calls 'contains' in the background
767    /// if 4 in x {
768    ///     print("found!");
769    /// }
770    /// ```
771    #[rhai_fn(return_raw, pure)]
772    pub fn contains(
773        ctx: NativeCallContext,
774        array: &mut Array,
775        value: Dynamic,
776    ) -> RhaiResultOf<bool> {
777        if array.is_empty() {
778            return Ok(false);
779        }
780
781        for item in array {
782            if ctx
783                .call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
784                .or_else(|err| match *err {
785                    ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
786                        if item.type_id() == value.type_id() {
787                            // No default when comparing same type
788                            Err(err)
789                        } else {
790                            Ok(Dynamic::FALSE)
791                        }
792                    }
793                    _ => Err(err),
794                })?
795                .as_bool()
796                .unwrap_or(false)
797            {
798                return Ok(true);
799            }
800        }
801
802        Ok(false)
803    }
804    /// Find the first element in the array that equals a particular `value` and return its index.
805    /// If no element equals `value`, `-1` is returned.
806    ///
807    /// The operator `==` is used to compare elements with `value` and must be defined,
808    /// otherwise `false` is assumed.
809    ///
810    /// # Example
811    ///
812    /// ```rhai
813    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
814    ///
815    /// print(x.index_of(4));       // prints 3 (first index)
816    ///
817    /// print(x.index_of(9));       // prints -1
818    ///
819    /// print(x.index_of("foo"));   // prints -1: strings do not equal numbers
820    /// ```
821    #[rhai_fn(return_raw, pure)]
822    pub fn index_of(
823        ctx: NativeCallContext,
824        array: &mut Array,
825        value: Dynamic,
826    ) -> RhaiResultOf<INT> {
827        if array.is_empty() {
828            return Ok(-1);
829        }
830
831        index_of_starting_from(ctx, array, value, 0)
832    }
833    /// Find the first element in the array, starting from a particular `start` position, that
834    /// equals a particular `value` and return its index. If no element equals `value`, `-1` is returned.
835    ///
836    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
837    /// * If `start` < -length of array, position counts from the beginning of the array.
838    /// * If `start` ≥ length of array, `-1` is returned.
839    ///
840    /// The operator `==` is used to compare elements with `value` and must be defined,
841    /// otherwise `false` is assumed.
842    ///
843    /// # Example
844    ///
845    /// ```rhai
846    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
847    ///
848    /// print(x.index_of(4, 2));        // prints 3
849    ///
850    /// print(x.index_of(4, 5));        // prints 7
851    ///
852    /// print(x.index_of(4, 15));       // prints -1: nothing found past end of array
853    ///
854    /// print(x.index_of(4, -5));       // prints 11: -5 = start from index 8
855    ///
856    /// print(x.index_of(9, 1));        // prints -1: nothing equals 9
857    ///
858    /// print(x.index_of("foo", 1));    // prints -1: strings do not equal numbers
859    /// ```
860    #[rhai_fn(name = "index_of", return_raw, pure)]
861    pub fn index_of_starting_from(
862        ctx: NativeCallContext,
863        array: &mut Array,
864        value: Dynamic,
865        start: INT,
866    ) -> RhaiResultOf<INT> {
867        if array.is_empty() {
868            return Ok(-1);
869        }
870
871        let (start, ..) = calc_offset_len(array.len(), start, 0);
872
873        for (i, item) in array.iter_mut().enumerate().skip(start) {
874            if ctx
875                .call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
876                .or_else(|err| match *err {
877                    ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
878                        if item.type_id() == value.type_id() {
879                            // No default when comparing same type
880                            Err(err)
881                        } else {
882                            Ok(Dynamic::FALSE)
883                        }
884                    }
885                    _ => Err(err),
886                })?
887                .as_bool()
888                .unwrap_or(false)
889            {
890                return Ok(i as INT);
891            }
892        }
893
894        Ok(-1 as INT)
895    }
896    /// Iterate through all the elements in the array, applying a `filter` function to each element
897    /// in turn, and return the index of the first element that returns `true`.
898    /// If no element returns `true`, `-1` is returned.
899    ///
900    /// # No Function Parameter
901    ///
902    /// Array element (mutable) is bound to `this`.
903    ///
904    /// This method is marked _pure_; the `filter` function should not mutate array elements.
905    ///
906    /// # Function Parameters
907    ///
908    /// * `element`: copy of array element
909    /// * `index` _(optional)_: current index in the array
910    ///
911    /// # Example
912    ///
913    /// ```rhai
914    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
915    ///
916    /// print(x.index_of(|v| v > 3));           // prints 3: 4 > 3
917    ///
918    /// print(x.index_of(|v| v > 8));           // prints -1: nothing is > 8
919    ///
920    /// print(x.index_of(|v, i| v * i > 20));   // prints 7: 4 * 7 > 20
921    /// ```
922    #[rhai_fn(name = "index_of", return_raw, pure)]
923    pub fn index_of_filter(
924        ctx: NativeCallContext,
925        array: &mut Array,
926        filter: FnPtr,
927    ) -> RhaiResultOf<INT> {
928        if array.is_empty() {
929            return Ok(-1);
930        }
931
932        index_of_filter_starting_from(ctx, array, filter, 0)
933    }
934    /// Iterate through all the elements in the array, starting from a particular `start` position,
935    /// applying a `filter` function to each element in turn, and return the index of the first
936    /// element that returns `true`. If no element returns `true`, `-1` is returned.
937    ///
938    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
939    /// * If `start` < -length of array, position counts from the beginning of the array.
940    /// * If `start` ≥ length of array, `-1` is returned.
941    ///
942    /// # No Function Parameter
943    ///
944    /// Array element (mutable) is bound to `this`.
945    ///
946    /// This method is marked _pure_; the `filter` function should not mutate array elements.
947    ///
948    /// # Function Parameters
949    ///
950    /// * `element`: copy of array element
951    /// * `index` _(optional)_: current index in the array
952    ///
953    /// # Example
954    ///
955    /// ```rhai
956    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
957    ///
958    /// print(x.index_of(|v| v > 1, 3));    // prints 5: 2 > 1
959    ///
960    /// print(x.index_of(|v| v < 2, 9));    // prints -1: nothing < 2 past index 9
961    ///
962    /// print(x.index_of(|v| v > 1, 15));   // prints -1: nothing found past end of array
963    ///
964    /// print(x.index_of(|v| v > 1, -5));   // prints 9: -5 = start from index 8
965    ///
966    /// print(x.index_of(|v| v > 1, -99));  // prints 1: -99 = start from beginning
967    ///
968    /// print(x.index_of(|v, i| v * i > 20, 8));    // prints 10: 3 * 10 > 20
969    /// ```
970    #[rhai_fn(name = "index_of", return_raw, pure)]
971    pub fn index_of_filter_starting_from(
972        ctx: NativeCallContext,
973        array: &mut Array,
974        filter: FnPtr,
975        start: INT,
976    ) -> RhaiResultOf<INT> {
977        if array.is_empty() {
978            return Ok(-1);
979        }
980
981        let (start, ..) = calc_offset_len(array.len(), start, 0);
982
983        for (i, item) in array.iter_mut().enumerate().skip(start) {
984            let ex = [(i as INT).into()];
985
986            if filter
987                .call_raw_with_extra_args("index_of", &ctx, Some(item), [], ex, Some(0))?
988                .as_bool()
989                .unwrap_or(false)
990            {
991                return Ok(i as INT);
992            }
993        }
994
995        Ok(-1 as INT)
996    }
997    /// Iterate through all the elements in the array, applying a `filter` function to each element
998    /// in turn, and return a copy of the first element that returns `true`. If no element returns
999    /// `true`, `()` is returned.
1000    ///
1001    /// # No Function Parameter
1002    ///
1003    /// Array element (mutable) is bound to `this`.
1004    ///
1005    /// # Function Parameters
1006    ///
1007    /// * `element`: copy of array element
1008    /// * `index` _(optional)_: current index in the array
1009    ///
1010    /// # Example
1011    ///
1012    /// ```rhai
1013    /// let x = [1, 2, 3, 5, 8, 13];
1014    ///
1015    /// print(x.find(|v| v > 3));                    // prints 5: 5 > 3
1016    ///
1017    /// print(x.find(|v| v > 13) ?? "not found");    // prints "not found": nothing is > 13
1018    ///
1019    /// print(x.find(|v, i| v * i > 13));            // prints 5: 3 * 5 > 13
1020    /// ```
1021    #[rhai_fn(return_raw, pure)]
1022    pub fn find(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResult {
1023        find_starting_from(ctx, array, filter, 0)
1024    }
1025    /// Iterate through all the elements in the array, starting from a particular `start` position,
1026    /// applying a `filter` function to each element in turn, and return a copy of the first element
1027    /// that returns `true`. If no element returns `true`, `()` is returned.
1028    ///
1029    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
1030    /// * If `start` < -length of array, position counts from the beginning of the array.
1031    /// * If `start` ≥ length of array, `-1` is returned.
1032    ///
1033    /// # No Function Parameter
1034    ///
1035    /// Array element (mutable) is bound to `this`.
1036    ///
1037    /// This method is marked _pure_; the `filter` function should not mutate array elements.
1038    ///
1039    /// # Function Parameters
1040    ///
1041    /// * `element`: copy of array element
1042    /// * `index` _(optional)_: current index in the array
1043    ///
1044    /// # Example
1045    ///
1046    /// ```rhai
1047    /// let x = [1, 2, 3, 5, 8, 13];
1048    ///
1049    /// print(x.find(|v| v > 1, 2));                     // prints 3: 3 > 1
1050    ///
1051    /// print(x.find(|v| v < 2, 3) ?? "not found");      // prints "not found": nothing < 2 past index 3
1052    ///
1053    /// print(x.find(|v| v > 1, 8) ?? "not found");      // prints "not found": nothing found past end of array
1054    ///
1055    /// print(x.find(|v| v > 1, -3));                    // prints 5: -3 = start from index 4
1056    ///
1057    /// print(x.find(|v| v > 0, -99));                   // prints 1: -99 = start from beginning
1058    ///
1059    /// print(x.find(|v, i| v * i > 6, 3));              // prints 5: 5 * 4 > 6
1060    /// ```
1061    #[rhai_fn(name = "find", return_raw, pure)]
1062    pub fn find_starting_from(
1063        ctx: NativeCallContext,
1064        array: &mut Array,
1065        filter: FnPtr,
1066        start: INT,
1067    ) -> RhaiResult {
1068        let index = index_of_filter_starting_from(ctx, array, filter, start)?;
1069
1070        if index < 0 {
1071            return Ok(Dynamic::UNIT);
1072        }
1073
1074        Ok(get(array, index))
1075    }
1076    /// Iterate through all the elements in the array, applying a `mapper` function to each element
1077    /// in turn, and return the first result that is not `()`. Otherwise, `()` is returned.
1078    ///
1079    /// # No Function Parameter
1080    ///
1081    /// Array element (mutable) is bound to `this`.
1082    ///
1083    /// This method is marked _pure_; the `mapper` function should not mutate array elements.
1084    ///
1085    /// # Function Parameters
1086    ///
1087    /// * `element`: copy of array element
1088    /// * `index` _(optional)_: current index in the array
1089    ///
1090    /// # Example
1091    ///
1092    /// ```rhai
1093    /// let x = [#{alice: 1}, #{bob: 2}, #{clara: 3}];
1094    ///
1095    /// print(x.find_map(|v| v.alice));                  // prints 1
1096    ///
1097    /// print(x.find_map(|v| v.dave) ?? "not found");    // prints "not found"
1098    ///
1099    /// print(x.find_map(|| this.dave) ?? "not found");  // prints "not found"
1100    /// ```
1101    #[rhai_fn(return_raw, pure)]
1102    pub fn find_map(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResult {
1103        find_map_starting_from(ctx, array, filter, 0)
1104    }
1105    /// Iterate through all the elements in the array, starting from a particular `start` position,
1106    /// applying a `mapper` function to each element in turn, and return the first result that is not `()`.
1107    /// Otherwise, `()` is returned.
1108    ///
1109    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
1110    /// * If `start` < -length of array, position counts from the beginning of the array.
1111    /// * If `start` ≥ length of array, `-1` is returned.
1112    ///
1113    /// # No Function Parameter
1114    ///
1115    /// Array element (mutable) is bound to `this`.
1116    ///
1117    /// This method is marked _pure_; the `mapper` function should not mutate array elements.
1118    ///
1119    /// # Function Parameters
1120    ///
1121    /// * `element`: copy of array element
1122    /// * `index` _(optional)_: current index in the array
1123    ///
1124    /// # Example
1125    ///
1126    /// ```rhai
1127    /// let x = [#{alice: 1}, #{bob: 2}, #{bob: 3}, #{clara: 3}, #{alice: 0}, #{clara: 5}];
1128    ///
1129    /// print(x.find_map(|v| v.alice, 2));                   // prints 0
1130    ///
1131    /// print(x.find_map(|v| v.bob, 4) ?? "not found");      // prints "not found"
1132    ///
1133    /// print(x.find_map(|v| v.alice, 8) ?? "not found");    // prints "not found"
1134    ///
1135    /// print(x.find_map(|| this.alice, 8) ?? "not found");  // prints "not found"
1136    ///
1137    /// print(x.find_map(|v| v.bob, -4));                    // prints 3: -4 = start from index 2
1138    ///
1139    /// print(x.find_map(|v| v.alice, -99));                 // prints 1: -99 = start from beginning
1140    ///
1141    /// print(x.find_map(|| this.alice, -99));               // prints 1: -99 = start from beginning
1142    /// ```
1143    #[rhai_fn(name = "find_map", return_raw, pure)]
1144    pub fn find_map_starting_from(
1145        ctx: NativeCallContext,
1146        array: &mut Array,
1147        filter: FnPtr,
1148        start: INT,
1149    ) -> RhaiResult {
1150        if array.is_empty() {
1151            return Ok(Dynamic::UNIT);
1152        }
1153
1154        let (start, ..) = calc_offset_len(array.len(), start, 0);
1155
1156        for (i, item) in array.iter_mut().enumerate().skip(start) {
1157            let ex = [(i as INT).into()];
1158
1159            let value =
1160                filter.call_raw_with_extra_args("find_map", &ctx, Some(item), [], ex, Some(0))?;
1161
1162            if !value.is_unit() {
1163                return Ok(value);
1164            }
1165        }
1166
1167        Ok(Dynamic::UNIT)
1168    }
1169    /// Return `true` if any element in the array that returns `true` when applied the `filter` function.
1170    ///
1171    /// # No Function Parameter
1172    ///
1173    /// Array element (mutable) is bound to `this`.
1174    ///
1175    /// This method is marked _pure_; the `filter` function should not mutate array elements.
1176    ///
1177    /// # Function Parameters
1178    ///
1179    /// * `element`: copy of array element
1180    /// * `index` _(optional)_: current index in the array
1181    ///
1182    /// # Example
1183    ///
1184    /// ```rhai
1185    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
1186    ///
1187    /// print(x.some(|v| v > 3));       // prints true
1188    ///
1189    /// print(x.some(|v| v > 10));      // prints false
1190    ///
1191    /// print(x.some(|v, i| i > v));    // prints true
1192    /// ```
1193    #[rhai_fn(return_raw, pure)]
1194    pub fn some(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<bool> {
1195        if array.is_empty() {
1196            return Ok(false);
1197        }
1198
1199        for (i, item) in array.iter_mut().enumerate() {
1200            let ex = [(i as INT).into()];
1201
1202            if filter
1203                .call_raw_with_extra_args("some", &ctx, Some(item), [], ex, Some(0))?
1204                .as_bool()
1205                .unwrap_or(false)
1206            {
1207                return Ok(true);
1208            }
1209        }
1210
1211        Ok(false)
1212    }
1213    /// Return `true` if all elements in the array return `true` when applied the `filter` function.
1214    ///
1215    /// # No Function Parameter
1216    ///
1217    /// Array element (mutable) is bound to `this`.
1218    ///
1219    /// This method is marked _pure_; the `filter` function should not mutate array elements.
1220    ///
1221    /// # Function Parameters
1222    ///
1223    /// * `element`: copy of array element
1224    /// * `index` _(optional)_: current index in the array
1225    ///
1226    /// # Example
1227    ///
1228    /// ```rhai
1229    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
1230    ///
1231    /// print(x.all(|v| v > 3));        // prints false
1232    ///
1233    /// print(x.all(|v| v > 1));        // prints true
1234    ///
1235    /// print(x.all(|v, i| i > v));     // prints false
1236    /// ```
1237    #[rhai_fn(return_raw, pure)]
1238    pub fn all(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<bool> {
1239        if array.is_empty() {
1240            return Ok(true);
1241        }
1242
1243        for (i, item) in array.iter_mut().enumerate() {
1244            let ex = [(i as INT).into()];
1245
1246            if !filter
1247                .call_raw_with_extra_args("all", &ctx, Some(item), [], ex, Some(0))?
1248                .as_bool()
1249                .unwrap_or(false)
1250            {
1251                return Ok(false);
1252            }
1253        }
1254
1255        Ok(true)
1256    }
1257    /// Remove duplicated _consecutive_ elements from the array.
1258    ///
1259    /// The operator `==` is used to compare elements and must be defined,
1260    /// otherwise `false` is assumed.
1261    ///
1262    /// # Example
1263    ///
1264    /// ```rhai
1265    /// let x = [1, 2, 2, 2, 3, 4, 3, 3, 2, 1];
1266    ///
1267    /// x.dedup();
1268    ///
1269    /// print(x);       // prints "[1, 2, 3, 4, 3, 2, 1]"
1270    /// ```
1271    pub fn dedup(ctx: NativeCallContext, array: &mut Array) {
1272        let comparer = FnPtr {
1273            name: ctx.engine().get_interned_string(OP_EQUALS),
1274            curry: <_>::default(),
1275            environ: None,
1276            #[cfg(not(feature = "no_function"))]
1277            fn_def: None,
1278        };
1279        dedup_by_comparer(ctx, array, comparer);
1280    }
1281    /// Remove duplicated _consecutive_ elements from the array that return `true` when applied the
1282    /// `comparer` function.
1283    ///
1284    /// No element is removed if the correct `comparer` function does not exist.
1285    ///
1286    /// # Function Parameters
1287    ///
1288    /// * `element1`: copy of the current array element to compare
1289    /// * `element2`: copy of the next array element to compare
1290    ///
1291    /// ## Return Value
1292    ///
1293    /// `true` if `element1 == element2`, otherwise `false`.
1294    ///
1295    /// # Example
1296    ///
1297    /// ```rhai
1298    /// let x = [1, 2, 2, 2, 3, 1, 2, 3, 4, 3, 3, 2, 1];
1299    ///
1300    /// x.dedup(|a, b| a >= b);
1301    ///
1302    /// print(x);       // prints "[1, 2, 3, 4]"
1303    /// ```
1304    #[rhai_fn(name = "dedup")]
1305    pub fn dedup_by_comparer(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) {
1306        if array.is_empty() {
1307            return;
1308        }
1309
1310        array.dedup_by(|x, y| {
1311            comparer
1312                .call_raw(&ctx, None, [y.clone(), x.clone()])
1313                .unwrap_or(Dynamic::FALSE)
1314                .as_bool()
1315                .unwrap_or(false)
1316        });
1317    }
1318    /// Reduce an array by iterating through all elements while applying the `reducer` function.
1319    ///
1320    /// # Function Parameters
1321    ///
1322    /// * `result`: accumulated result, initially `()`
1323    /// * `element`: copy of array element, or bound to `this` if omitted
1324    /// * `index` _(optional)_: current index in the array
1325    ///
1326    /// This method is marked _pure_; the `reducer` function should not mutate array elements.
1327    ///
1328    /// # Example
1329    ///
1330    /// ```rhai
1331    /// let x = [1, 2, 3, 4, 5];
1332    ///
1333    /// let y = x.reduce(|r, v| v + (r ?? 0));
1334    ///
1335    /// print(y);       // prints 15
1336    ///
1337    /// let y = x.reduce(|r, v, i| v + i + (r ?? 0));
1338    ///
1339    /// print(y);       // prints 25
1340    /// ```
1341    #[rhai_fn(return_raw, pure)]
1342    pub fn reduce(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult {
1343        reduce_with_initial(ctx, array, reducer, Dynamic::UNIT)
1344    }
1345    /// Reduce an array by iterating through all elements while applying the `reducer` function.
1346    ///
1347    /// # Function Parameters
1348    ///
1349    /// * `result`: accumulated result, starting with the value of `initial`
1350    /// * `element`: copy of array element, or bound to `this` if omitted
1351    /// * `index` _(optional)_: current index in the array
1352    ///
1353    /// This method is marked _pure_; the `reducer` function should not mutate array elements.
1354    ///
1355    /// # Example
1356    ///
1357    /// ```rhai
1358    /// let x = [1, 2, 3, 4, 5];
1359    ///
1360    /// let y = x.reduce(|r, v| v + r, 5);
1361    ///
1362    /// print(y);       // prints 20
1363    ///
1364    /// let y = x.reduce(|r, v, i| v + i + r, 5);
1365    ///
1366    /// print(y);       // prints 30
1367    /// ```
1368    #[rhai_fn(name = "reduce", return_raw, pure)]
1369    pub fn reduce_with_initial(
1370        ctx: NativeCallContext,
1371        array: &mut Array,
1372        reducer: FnPtr,
1373        initial: Dynamic,
1374    ) -> RhaiResult {
1375        if array.is_empty() {
1376            return Ok(initial);
1377        }
1378
1379        array
1380            .iter_mut()
1381            .enumerate()
1382            .try_fold(initial, |result, (i, item)| {
1383                let ex = [(i as INT).into()];
1384                reducer.call_raw_with_extra_args("reduce", &ctx, Some(item), [result], ex, Some(1))
1385            })
1386    }
1387    /// Reduce an array by iterating through all elements, in _reverse_ order,
1388    /// while applying the `reducer` function.
1389    ///
1390    /// # Function Parameters
1391    ///
1392    /// * `result`: accumulated result, initially `()`
1393    /// * `element`: copy of array element, or bound to `this` if omitted
1394    /// * `index` _(optional)_: current index in the array
1395    ///
1396    /// This method is marked _pure_; the `reducer` function should not mutate array elements.
1397    ///
1398    /// # Example
1399    ///
1400    /// ```rhai
1401    /// let x = [1, 2, 3, 4, 5];
1402    ///
1403    /// let y = x.reduce_rev(|r, v| v + (r ?? 0));
1404    ///
1405    /// print(y);       // prints 15
1406    ///
1407    /// let y = x.reduce_rev(|r, v, i| v + i + (r ?? 0));
1408    ///
1409    /// print(y);       // prints 25
1410    /// ```
1411    #[rhai_fn(return_raw, pure)]
1412    pub fn reduce_rev(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult {
1413        reduce_rev_with_initial(ctx, array, reducer, Dynamic::UNIT)
1414    }
1415    /// Reduce an array by iterating through all elements, in _reverse_ order,
1416    /// while applying the `reducer` function.
1417    ///
1418    /// # Function Parameters
1419    ///
1420    /// * `result`: accumulated result, starting with the value of `initial`
1421    /// * `element`: copy of array element, or bound to `this` if omitted
1422    /// * `index` _(optional)_: current index in the array
1423    ///
1424    /// This method is marked _pure_; the `reducer` function should not mutate array elements.
1425    ///
1426    /// # Example
1427    ///
1428    /// ```rhai
1429    /// let x = [1, 2, 3, 4, 5];
1430    ///
1431    /// let y = x.reduce_rev(|r, v| v + r, 5);
1432    ///
1433    /// print(y);       // prints 20
1434    ///
1435    /// let y = x.reduce_rev(|r, v, i| v + i + r, 5);
1436    ///
1437    /// print(y);       // prints 30
1438    /// ```
1439    #[rhai_fn(name = "reduce_rev", return_raw, pure)]
1440    pub fn reduce_rev_with_initial(
1441        ctx: NativeCallContext,
1442        array: &mut Array,
1443        reducer: FnPtr,
1444        initial: Dynamic,
1445    ) -> RhaiResult {
1446        if array.is_empty() {
1447            return Ok(initial);
1448        }
1449
1450        let len = array.len();
1451
1452        array
1453            .iter_mut()
1454            .rev()
1455            .enumerate()
1456            .try_fold(initial, |result, (i, item)| {
1457                let ex = [((len - 1 - i) as INT).into()];
1458
1459                reducer.call_raw_with_extra_args(
1460                    "reduce_rev",
1461                    &ctx,
1462                    Some(item),
1463                    [result],
1464                    ex,
1465                    Some(1),
1466                )
1467            })
1468    }
1469    /// Iterate through all elements in two arrays, applying a `mapper` function to them,
1470    /// and return a new array containing the results.
1471    ///
1472    /// # Function Parameters
1473    ///
1474    /// * `array1`: First array
1475    /// * `array2`: Second array
1476    /// * `index` _(optional)_: current index in the array
1477    ///
1478    /// # Example
1479    ///
1480    /// ```rhai
1481    /// let x = [1, 2, 3, 4, 5];
1482    /// let y = [9, 8, 7, 6];
1483    ///
1484    /// let z = x.zip(y, |a, b| a + b);
1485    ///
1486    /// print(z);       // prints [10, 10, 10, 10]
1487    ///
1488    /// let z = x.zip(y, |a, b, i| a + b + i);
1489    ///
1490    /// print(z);       // prints [10, 11, 12, 13]
1491    /// ```
1492    #[rhai_fn(return_raw, pure)]
1493    pub fn zip(
1494        ctx: NativeCallContext,
1495        array1: &mut Array,
1496        array2: Array,
1497        map: FnPtr,
1498    ) -> RhaiResultOf<Array> {
1499        if array1.is_empty() && array2.is_empty() {
1500            return Ok(Array::new());
1501        }
1502
1503        array1
1504            .iter_mut()
1505            .zip(array2)
1506            .enumerate()
1507            .map(|(i, (x, y))| {
1508                map.call_raw_with_extra_args(
1509                    "zip",
1510                    &ctx,
1511                    None,
1512                    [x.clone(), y],
1513                    [(i as INT).into()],
1514                    None,
1515                )
1516            })
1517            .collect()
1518    }
1519    /// Sort the array based on applying the `comparer` function.
1520    ///
1521    /// # Function Parameters
1522    ///
1523    /// * `element1`: copy of the current array element to compare
1524    /// * `element2`: copy of the next array element to compare
1525    ///
1526    /// ## Return Value
1527    ///
1528    /// An integer number:
1529    ///
1530    /// * Any positive integer if `element1 > element2`
1531    /// * 0 if `element1 == element2`
1532    /// * Any negative integer if `element1 < element2`
1533    ///
1534    /// or a boolean value:
1535    ///
1536    /// * `true` if `element1 <= element2`
1537    /// * `false` if `element1 > element2`
1538    ///
1539    /// Any other return value type will yield unpredictable order.
1540    ///
1541    /// # Example
1542    ///
1543    /// ```rhai
1544    /// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10];
1545    ///
1546    /// // Do comparisons in reverse
1547    /// x.sort(|a, b| if a > b { -1 } else if a < b { 1 } else { 0 });
1548    ///
1549    /// print(x);       // prints "[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]"
1550    /// ```
1551    pub fn sort(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) {
1552        if array.len() <= 1 {
1553            return;
1554        }
1555
1556        array.sort_by(|x, y| {
1557            comparer
1558                .call_raw(&ctx, None, [x.clone(), y.clone()])
1559                .ok()
1560                .and_then(|v| {
1561                    v.as_int()
1562                        .or_else(|_| v.as_bool().map(|v| v.then_some(-1).unwrap_or(1)))
1563                        .ok()
1564                })
1565                .map_or_else(
1566                    || x.type_id().cmp(&y.type_id()),
1567                    |v| match v {
1568                        v if v > 0 => Ordering::Greater,
1569                        v if v < 0 => Ordering::Less,
1570                        0 => Ordering::Equal,
1571                        _ => unreachable!("v is {}", v),
1572                    },
1573                )
1574        });
1575    }
1576    /// Sort the array.
1577    ///
1578    /// All elements in the array must be of the same data type.
1579    ///
1580    /// # Supported Data Types
1581    ///
1582    /// * integer numbers
1583    /// * floating-point numbers
1584    /// * decimal numbers
1585    /// * characters
1586    /// * strings
1587    /// * booleans
1588    /// * `()`
1589    ///
1590    /// # Example
1591    ///
1592    /// ```rhai
1593    /// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10];
1594    ///
1595    /// x.sort();
1596    ///
1597    /// print(x);       // prints "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
1598    /// ```
1599    #[rhai_fn(name = "sort", return_raw)]
1600    pub fn sort_with_builtin(array: &mut Array) -> RhaiResultOf<()> {
1601        if array.len() <= 1 {
1602            return Ok(());
1603        }
1604
1605        let type_id = array[0].type_id();
1606
1607        if array.iter().any(|a| a.type_id() != type_id) {
1608            return Err(ERR::ErrorFunctionNotFound(
1609                "sort() cannot be called with elements of different types".into(),
1610                Position::NONE,
1611            )
1612            .into());
1613        }
1614
1615        if type_id == TypeId::of::<INT>() {
1616            array.sort_by(|a, b| {
1617                let a = a.as_int().unwrap();
1618                let b = b.as_int().unwrap();
1619                a.cmp(&b)
1620            });
1621            return Ok(());
1622        }
1623        if type_id == TypeId::of::<char>() {
1624            array.sort_by(|a, b| {
1625                let a = a.as_char().unwrap();
1626                let b = b.as_char().unwrap();
1627                a.cmp(&b)
1628            });
1629            return Ok(());
1630        }
1631        #[cfg(not(feature = "no_float"))]
1632        if type_id == TypeId::of::<crate::FLOAT>() {
1633            array.sort_by(|a, b| {
1634                let a = a.as_float().unwrap();
1635                let b = b.as_float().unwrap();
1636                a.partial_cmp(&b).unwrap_or(Ordering::Equal)
1637            });
1638            return Ok(());
1639        }
1640        if type_id == TypeId::of::<ImmutableString>() {
1641            array.sort_by(|a, b| {
1642                let a = &*a.read_lock::<ImmutableString>().unwrap();
1643                let b = &*b.read_lock::<ImmutableString>().unwrap();
1644                a.cmp(b)
1645            });
1646            return Ok(());
1647        }
1648        #[cfg(feature = "decimal")]
1649        if type_id == TypeId::of::<rust_decimal::Decimal>() {
1650            array.sort_by(|a, b| {
1651                let a = a.as_decimal().unwrap();
1652                let b = b.as_decimal().unwrap();
1653                a.cmp(&b)
1654            });
1655            return Ok(());
1656        }
1657        if type_id == TypeId::of::<bool>() {
1658            array.sort_by(|a, b| {
1659                let a = a.as_bool().unwrap();
1660                let b = b.as_bool().unwrap();
1661                a.cmp(&b)
1662            });
1663            return Ok(());
1664        }
1665        if type_id == TypeId::of::<()>() {
1666            return Ok(());
1667        }
1668
1669        Ok(())
1670    }
1671    /// Remove all elements in the array that returns `true` when applied the `filter` function and
1672    /// return them as a new array.
1673    ///
1674    /// # No Function Parameter
1675    ///
1676    /// Array element (mutable) is bound to `this`.
1677    ///
1678    /// # Function Parameters
1679    ///
1680    /// * `element`: copy of array element
1681    /// * `index` _(optional)_: current index in the array
1682    ///
1683    /// # Example
1684    ///
1685    /// ```rhai
1686    /// let x = [1, 2, 3, 4, 5];
1687    ///
1688    /// let y = x.drain(|v| v < 3);
1689    ///
1690    /// print(x);       // prints "[3, 4, 5]"
1691    ///
1692    /// print(y);       // prints "[1, 2]"
1693    ///
1694    /// let z = x.drain(|v, i| v + i > 5);
1695    ///
1696    /// print(x);       // prints "[3, 4]"
1697    ///
1698    /// print(z);       // prints "[5]"
1699    /// ```
1700    #[rhai_fn(return_raw)]
1701    pub fn drain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
1702        if array.is_empty() {
1703            return Ok(Array::new());
1704        }
1705
1706        let mut drained = Array::with_capacity(array.len());
1707
1708        let mut i = 0;
1709        let mut x = 0;
1710
1711        while x < array.len() {
1712            let ex = [(i as INT).into()];
1713
1714            if filter
1715                .call_raw_with_extra_args("drain", &ctx, Some(&mut array[x]), [], ex, Some(0))?
1716                .as_bool()
1717                .unwrap_or(false)
1718            {
1719                drained.push(array.remove(x));
1720            } else {
1721                x += 1;
1722            }
1723
1724            i += 1;
1725        }
1726
1727        Ok(drained)
1728    }
1729    /// Remove all elements in the array within an exclusive `range` and return them as a new array.
1730    ///
1731    /// # Example
1732    ///
1733    /// ```rhai
1734    /// let x = [1, 2, 3, 4, 5];
1735    ///
1736    /// let y = x.drain(1..3);
1737    ///
1738    /// print(x);       // prints "[1, 4, 5]"
1739    ///
1740    /// print(y);       // prints "[2, 3]"
1741    ///
1742    /// let z = x.drain(2..3);
1743    ///
1744    /// print(x);       // prints "[1, 4]"
1745    ///
1746    /// print(z);       // prints "[5]"
1747    /// ```
1748    #[rhai_fn(name = "drain")]
1749    pub fn drain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array {
1750        let start = INT::max(range.start, 0);
1751        let end = INT::max(range.end, start);
1752        drain_range(array, start, end - start)
1753    }
1754    /// Remove all elements in the array within an inclusive `range` and return them as a new array.
1755    ///
1756    /// # Example
1757    ///
1758    /// ```rhai
1759    /// let x = [1, 2, 3, 4, 5];
1760    ///
1761    /// let y = x.drain(1..=2);
1762    ///
1763    /// print(x);       // prints "[1, 4, 5]"
1764    ///
1765    /// print(y);       // prints "[2, 3]"
1766    ///
1767    /// let z = x.drain(2..=2);
1768    ///
1769    /// print(x);       // prints "[1, 4]"
1770    ///
1771    /// print(z);       // prints "[5]"
1772    /// ```
1773    #[rhai_fn(name = "drain")]
1774    pub fn drain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
1775        let start = INT::max(*range.start(), 0);
1776        let end = INT::max(*range.end(), start);
1777        drain_range(array, start, end - start + 1)
1778    }
1779    /// Remove all elements within a portion of the array and return them as a new array.
1780    ///
1781    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
1782    /// * If `start` < -length of array, position counts from the beginning of the array.
1783    /// * If `start` ≥ length of array, no element is removed and an empty array is returned.
1784    /// * If `len` ≤ 0, no element is removed and an empty array is returned.
1785    /// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is removed and returned.
1786    ///
1787    /// # Example
1788    ///
1789    /// ```rhai
1790    /// let x = [1, 2, 3, 4, 5];
1791    ///
1792    /// let y = x.drain(1, 2);
1793    ///
1794    /// print(x);       // prints "[1, 4, 5]"
1795    ///
1796    /// print(y);       // prints "[2, 3]"
1797    ///
1798    /// let z = x.drain(-1, 1);
1799    ///
1800    /// print(x);       // prints "[1, 4]"
1801    ///
1802    /// print(z);       // prints "[5]"
1803    /// ```
1804    #[rhai_fn(name = "drain")]
1805    pub fn drain_range(array: &mut Array, start: INT, len: INT) -> Array {
1806        if array.is_empty() || len <= 0 {
1807            return Array::new();
1808        }
1809
1810        let (start, len) = calc_offset_len(array.len(), start, len);
1811
1812        if len == 0 {
1813            return Array::new();
1814        }
1815
1816        array.drain(start..start + len).collect()
1817    }
1818    /// Remove all elements in the array that do not return `true` when applied the `filter`
1819    /// function and return them as a new array.
1820    ///
1821    /// # No Function Parameter
1822    ///
1823    /// Array element (mutable) is bound to `this`.
1824    ///
1825    /// # Function Parameters
1826    ///
1827    /// * `element`: copy of array element
1828    /// * `index` _(optional)_: current index in the array
1829    ///
1830    /// # Example
1831    ///
1832    /// ```rhai
1833    /// let x = [1, 2, 3, 4, 5];
1834    ///
1835    /// let y = x.retain(|v| v >= 3);
1836    ///
1837    /// print(x);       // prints "[3, 4, 5]"
1838    ///
1839    /// print(y);       // prints "[1, 2]"
1840    ///
1841    /// let z = x.retain(|v, i| v + i <= 5);
1842    ///
1843    /// print(x);       // prints "[3, 4]"
1844    ///
1845    /// print(z);       // prints "[5]"
1846    /// ```
1847    #[rhai_fn(return_raw)]
1848    pub fn retain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
1849        if array.is_empty() {
1850            return Ok(Array::new());
1851        }
1852
1853        let mut drained = Array::new();
1854
1855        let mut i = 0;
1856        let mut x = 0;
1857
1858        while x < array.len() {
1859            let ex = [(i as INT).into()];
1860
1861            if filter
1862                .call_raw_with_extra_args("retain", &ctx, Some(&mut array[x]), [], ex, Some(0))?
1863                .as_bool()
1864                .unwrap_or(false)
1865            {
1866                x += 1;
1867            } else {
1868                drained.push(array.remove(x));
1869            }
1870
1871            i += 1;
1872        }
1873
1874        Ok(drained)
1875    }
1876    /// Remove all elements in the array not within an exclusive `range` and return them as a new array.
1877    ///
1878    /// # Example
1879    ///
1880    /// ```rhai
1881    /// let x = [1, 2, 3, 4, 5];
1882    ///
1883    /// let y = x.retain(1..4);
1884    ///
1885    /// print(x);       // prints "[2, 3, 4]"
1886    ///
1887    /// print(y);       // prints "[1, 5]"
1888    ///
1889    /// let z = x.retain(1..3);
1890    ///
1891    /// print(x);       // prints "[3, 4]"
1892    ///
1893    /// print(z);       // prints "[1]"
1894    /// ```
1895    #[rhai_fn(name = "retain")]
1896    pub fn retain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array {
1897        let start = INT::max(range.start, 0);
1898        let end = INT::max(range.end, start);
1899        retain_range(array, start, end - start)
1900    }
1901    /// Remove all elements in the array not within an inclusive `range` and return them as a new array.
1902    ///
1903    /// # Example
1904    ///
1905    /// ```rhai
1906    /// let x = [1, 2, 3, 4, 5];
1907    ///
1908    /// let y = x.retain(1..=3);
1909    ///
1910    /// print(x);       // prints "[2, 3, 4]"
1911    ///
1912    /// print(y);       // prints "[1, 5]"
1913    ///
1914    /// let z = x.retain(1..=2);
1915    ///
1916    /// print(x);       // prints "[3, 4]"
1917    ///
1918    /// print(z);       // prints "[1]"
1919    /// ```
1920    #[rhai_fn(name = "retain")]
1921    pub fn retain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
1922        let start = INT::max(*range.start(), 0);
1923        let end = INT::max(*range.end(), start);
1924        retain_range(array, start, end - start + 1)
1925    }
1926    /// Remove all elements not within a portion of the array and return them as a new array.
1927    ///
1928    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
1929    /// * If `start` < -length of array, position counts from the beginning of the array.
1930    /// * If `start` ≥ length of array, all elements are removed returned.
1931    /// * If `len` ≤ 0, all elements are removed and returned.
1932    /// * If `start` position + `len` ≥ length of array, entire portion of the array before the `start` position is removed and returned.
1933    ///
1934    /// # Example
1935    ///
1936    /// ```rhai
1937    /// let x = [1, 2, 3, 4, 5];
1938    ///
1939    /// let y = x.retain(1, 2);
1940    ///
1941    /// print(x);       // prints "[2, 3]"
1942    ///
1943    /// print(y);       // prints "[1, 4, 5]"
1944    ///
1945    /// let z = x.retain(-1, 1);
1946    ///
1947    /// print(x);       // prints "[3]"
1948    ///
1949    /// print(z);       // prints "[2]"
1950    /// ```
1951    #[rhai_fn(name = "retain")]
1952    pub fn retain_range(array: &mut Array, start: INT, len: INT) -> Array {
1953        if array.is_empty() || len <= 0 {
1954            return Array::new();
1955        }
1956
1957        let (start, len) = calc_offset_len(array.len(), start, len);
1958
1959        if len == 0 {
1960            return Array::new();
1961        }
1962
1963        let mut drained: Array = array.drain(..start).collect();
1964        drained.extend(array.drain(len..));
1965
1966        drained
1967    }
1968    /// Return `true` if two arrays are equal (i.e. all elements are equal and in the same order).
1969    ///
1970    /// The operator `==` is used to compare elements and must be defined,
1971    /// otherwise `false` is assumed.
1972    ///
1973    /// # Example
1974    ///
1975    /// ```rhai
1976    /// let x = [1, 2, 3, 4, 5];
1977    /// let y = [1, 2, 3, 4, 5];
1978    /// let z = [1, 2, 3, 4];
1979    ///
1980    /// print(x == y);      // prints true
1981    ///
1982    /// print(x == z);      // prints false
1983    /// ```
1984    #[rhai_fn(name = "==", return_raw, pure)]
1985    pub fn equals(ctx: NativeCallContext, array1: &mut Array, array2: Array) -> RhaiResultOf<bool> {
1986        if array1.len() != array2.len() {
1987            return Ok(false);
1988        }
1989        if array1.is_empty() {
1990            return Ok(true);
1991        }
1992
1993        let mut array2 = array2;
1994
1995        for (a1, a2) in array1.iter_mut().zip(array2.iter_mut()) {
1996            if !ctx
1997                .call_native_fn_raw(OP_EQUALS, true, &mut [a1, a2])
1998                .or_else(|err| match *err {
1999                    ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
2000                        if a1.type_id() == a2.type_id() {
2001                            // No default when comparing same type
2002                            Err(err)
2003                        } else {
2004                            Ok(Dynamic::FALSE)
2005                        }
2006                    }
2007                    _ => Err(err),
2008                })?
2009                .as_bool()
2010                .unwrap_or(false)
2011            {
2012                return Ok(false);
2013            }
2014        }
2015
2016        Ok(true)
2017    }
2018    /// Return `true` if two arrays are not-equal (i.e. any element not equal or not in the same order).
2019    ///
2020    /// The operator `==` is used to compare elements and must be defined,
2021    /// otherwise `false` is assumed.
2022    ///
2023    /// # Example
2024    ///
2025    /// ```rhai
2026    /// let x = [1, 2, 3, 4, 5];
2027    /// let y = [1, 2, 3, 4, 5];
2028    /// let z = [1, 2, 3, 4];
2029    ///
2030    /// print(x != y);      // prints false
2031    ///
2032    /// print(x != z);      // prints true
2033    /// ```
2034    #[rhai_fn(name = "!=", return_raw, pure)]
2035    pub fn not_equals(
2036        ctx: NativeCallContext,
2037        array1: &mut Array,
2038        array2: Array,
2039    ) -> RhaiResultOf<bool> {
2040        equals(ctx, array1, array2).map(|r| !r)
2041    }
2042}