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}