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