rhai/src/packages/array_basic.rs

1960 lines
60 KiB
Rust
Raw Normal View History

#![cfg(not(feature = "no_index"))]
use crate::api::deprecated::deprecated_array_functions;
2021-03-23 05:13:53 +01:00
use crate::engine::OP_EQUALS;
use crate::eval::{calc_index, calc_offset_len, calc_array_sizes};
2022-11-23 04:36:30 +01:00
use crate::module::ModuleFlags;
2020-08-14 12:58:34 +02:00
use crate::plugin::*;
2021-12-15 05:06:17 +01:00
use crate::{
2022-01-16 16:15:37 +01:00
def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext,
2022-08-27 10:26:41 +02:00
Position, RhaiResultOf, StaticVec, ERR, INT, MAX_USIZE_INT,
2021-12-15 05:06:17 +01:00
};
2021-04-17 09:15:54 +02:00
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{any::TypeId, cmp::Ordering, mem};
2020-08-14 12:58:34 +02:00
2021-12-20 04:42:39 +01:00
def_package! {
/// Package of basic array utilities.
pub BasicArrayPackage(lib) {
2022-11-23 04:36:30 +01:00
lib.flags |= ModuleFlags::STANDARD_LIB;
2021-12-20 04:42:39 +01:00
combine_with_exported_module!(lib, "array", array_functions);
combine_with_exported_module!(lib, "deprecated_array", deprecated_array_functions);
2020-09-21 10:15:52 +02:00
2021-12-20 04:42:39 +01:00
// Register array iterator
lib.set_iterable::<Array>();
}
}
2020-08-14 12:58:34 +02:00
#[export_module]
2021-12-22 12:59:48 +01:00
pub mod array_functions {
/// Number of elements in the array.
#[rhai_fn(name = "len", get = "len", pure)]
pub fn len(array: &mut Array) -> INT {
array.len() as INT
2020-08-14 12:58:34 +02:00
}
/// Return true if the array is empty.
#[rhai_fn(name = "is_empty", get = "is_empty", pure)]
pub fn is_empty(array: &mut Array) -> bool {
array.len() == 0
}
/// Get a copy of the element at the `index` position in the array.
///
/// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `index` < -length of array, `()` is returned.
/// * If `index` ≥ length of array, `()` is returned.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3];
///
/// print(x.get(0)); // prints 1
///
/// print(x.get(-1)); // prints 3
///
/// print(x.get(99)); // prints empty (for '()')
/// ```
pub fn get(array: &mut Array, index: INT) -> Dynamic {
if array.is_empty() {
return Dynamic::UNIT;
}
2022-02-08 02:02:15 +01:00
let (index, ..) = calc_offset_len(array.len(), index, 0);
if index >= array.len() {
Dynamic::UNIT
} else {
array[index].clone()
}
}
/// Set the element at the `index` position in the array to a new `value`.
///
/// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `index` < -length of array, the array is not modified.
/// * If `index` ≥ length of array, the array is not modified.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3];
///
/// x.set(0, 42);
///
/// print(x); // prints "[42, 2, 3]"
///
/// x.set(-3, 0);
///
/// print(x); // prints "[0, 2, 3]"
///
/// x.set(99, 123);
///
/// print(x); // prints "[0, 2, 3]"
/// ```
pub fn set(array: &mut Array, index: INT, value: Dynamic) {
if array.is_empty() {
return;
}
2022-02-08 02:02:15 +01:00
let (index, ..) = calc_offset_len(array.len(), index, 0);
if index < array.len() {
array[index] = value;
}
}
/// Add a new element, which is not another array, to the end of the array.
///
/// If `item` is `Array`, then `append` is more specific and will be called instead.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3];
///
/// x.push("hello");
///
/// print(x); // prints [1, 2, 3, "hello"]
/// ```
pub fn push(array: &mut Array, item: Dynamic) {
array.push(item);
}
/// Add all the elements of another array to the end of the array.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3];
/// let y = [true, 'x'];
///
2022-09-03 05:27:13 +02:00
/// x.append(y);
///
/// print(x); // prints "[1, 2, 3, true, 'x']"
/// ```
pub fn append(array: &mut Array, new_array: Array) {
if !new_array.is_empty() {
if array.is_empty() {
*array = new_array;
} else {
array.extend(new_array);
}
}
2020-08-14 12:58:34 +02:00
}
/// Combine two arrays into a new array and return it.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3];
/// let y = [true, 'x'];
///
/// print(x + y); // prints "[1, 2, 3, true, 'x']"
///
/// print(x); // prints "[1, 2, 3"
/// ```
#[rhai_fn(name = "+")]
2021-12-22 12:59:48 +01:00
pub fn concat(array1: Array, array2: Array) -> Array {
2022-07-27 12:04:59 +02:00
if array2.is_empty() {
2021-12-22 12:59:48 +01:00
array1
2022-07-27 12:04:59 +02:00
} else if array1.is_empty() {
array2
} else {
let mut array = array1;
array.extend(array2);
array
}
}
/// Add a new element into the array at a particular `index` position.
///
/// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `index` < -length of array, the element is added to the beginning of the array.
/// * If `index` ≥ length of array, the element is appended to the end of the array.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3];
///
/// x.insert(0, "hello");
///
/// x.insert(2, true);
///
/// x.insert(-2, 42);
///
/// print(x); // prints ["hello", 1, true, 2, 42, 3]
/// ```
pub fn insert(array: &mut Array, index: INT, item: Dynamic) {
if array.is_empty() {
array.push(item);
return;
}
2022-02-08 02:02:15 +01:00
let (index, ..) = calc_offset_len(array.len(), index, 0);
if index >= array.len() {
array.push(item);
} else {
array.insert(index, item);
}
}
/// Pad the array to at least the specified length with copies of a specified element.
///
/// If `len` ≤ length of array, no padding is done.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3];
///
/// x.pad(5, 42);
///
/// print(x); // prints "[1, 2, 3, 42, 42]"
///
/// x.pad(3, 123);
///
/// print(x); // prints "[1, 2, 3, 42, 42]"
/// ```
#[rhai_fn(return_raw)]
pub fn pad(
2021-11-23 11:10:01 +01:00
ctx: NativeCallContext,
array: &mut Array,
len: INT,
item: Dynamic,
2021-12-25 16:49:14 +01:00
) -> RhaiResultOf<()> {
2022-12-22 10:34:58 +01:00
if len <= 0 {
return Ok(());
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let len = len.min(MAX_USIZE_INT) as usize;
2022-08-27 10:26:41 +02:00
2022-12-22 10:34:58 +01:00
if len <= array.len() {
return Ok(());
}
2021-11-23 11:10:01 +01:00
let _ctx = ctx;
// Check if array will be over max size limit
#[cfg(not(feature = "unchecked"))]
2022-12-26 10:32:46 +01:00
if _ctx.engine().max_array_size() > 0 {
let pad = len - array.len();
let (a, m, s) = calc_array_sizes(array);
2022-12-26 10:32:46 +01:00
let (ax, mx, sx) = item.calc_data_sizes(true);
2022-11-24 04:00:47 +01:00
2022-12-26 10:32:46 +01:00
_ctx.engine()
.throw_on_size((a + pad + ax * pad, m + mx * pad, s + sx * pad))?;
}
array.resize(len, item);
2021-03-22 04:18:09 +01:00
Ok(())
}
/// Remove the last element from the array and return it.
///
/// If the array is empty, `()` is returned.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3];
///
/// print(x.pop()); // prints 3
///
/// print(x); // prints "[1, 2]"
/// ```
pub fn pop(array: &mut Array) -> Dynamic {
if array.is_empty() {
Dynamic::UNIT
} else {
2022-07-27 10:04:24 +02:00
array.pop().unwrap_or(Dynamic::UNIT)
}
2020-08-14 12:58:34 +02:00
}
/// Remove the first element from the array and return it.
///
/// If the array is empty, `()` is returned.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3];
///
/// print(x.shift()); // prints 1
///
/// print(x); // prints "[2, 3]"
/// ```
pub fn shift(array: &mut Array) -> Dynamic {
if array.is_empty() {
Dynamic::UNIT
2020-08-14 12:58:34 +02:00
} else {
array.remove(0)
2020-08-14 12:58:34 +02:00
}
}
/// Remove the element at the specified `index` from the array and return it.
///
/// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `index` < -length of array, `()` is returned.
/// * If `index` ≥ length of array, `()` is returned.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3];
///
/// print(x.remove(1)); // prints 2
///
/// print(x); // prints "[1, 3]"
///
/// print(x.remove(-2)); // prints 1
///
/// print(x); // prints "[3]"
/// ```
pub fn remove(array: &mut Array, index: INT) -> Dynamic {
let index = match calc_index(array.len(), index, true, || Err(())) {
Ok(n) => n,
Err(_) => return Dynamic::UNIT,
};
array.remove(index)
2020-08-14 12:58:34 +02:00
}
/// Clear the array.
pub fn clear(array: &mut Array) {
if !array.is_empty() {
array.clear();
}
2020-08-14 12:58:34 +02:00
}
/// Cut off the array at the specified length.
///
/// * If `len` ≤ 0, the array is cleared.
/// * If `len` ≥ length of array, the array is not truncated.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// x.truncate(3);
///
/// print(x); // prints "[1, 2, 3]"
///
/// x.truncate(10);
///
/// print(x); // prints "[1, 2, 3]"
/// ```
pub fn truncate(array: &mut Array, len: INT) {
2022-12-22 10:34:58 +01:00
if len <= 0 {
array.clear();
return;
}
if !array.is_empty() {
2022-12-22 10:34:58 +01:00
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let len = len.min(MAX_USIZE_INT) as usize;
2022-08-27 10:26:41 +02:00
if len > 0 {
2022-12-22 10:34:58 +01:00
array.truncate(len);
} else {
array.clear();
}
2020-08-14 12:58:34 +02:00
}
}
/// Cut off the head of the array, leaving a tail of the specified length.
///
/// * If `len` ≤ 0, the array is cleared.
/// * If `len` ≥ length of array, the array is not modified.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// x.chop(3);
///
/// print(x); // prints "[3, 4, 5]"
///
/// x.chop(10);
///
/// print(x); // prints "[3, 4, 5]"
/// ```
pub fn chop(array: &mut Array, len: INT) {
2022-12-22 10:34:58 +01:00
if len <= 0 {
array.clear();
return;
}
2022-01-14 03:04:24 +01:00
if !array.is_empty() {
2022-12-22 10:34:58 +01:00
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let len = len.min(MAX_USIZE_INT) as usize;
2022-08-27 10:26:41 +02:00
2022-01-14 03:04:24 +01:00
if len <= 0 {
array.clear();
2022-12-22 10:34:58 +01:00
} else if len < array.len() {
array.drain(0..array.len() - len);
}
2020-10-13 08:39:49 +02:00
}
}
/// Reverse all the elements in the array.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// x.reverse();
///
/// print(x); // prints "[5, 4, 3, 2, 1]"
/// ```
pub fn reverse(array: &mut Array) {
if !array.is_empty() {
array.reverse();
}
2020-09-24 04:51:21 +02:00
}
/// Replace an exclusive range of the array with another array.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
/// let y = [7, 8, 9, 10];
///
/// x.splice(1..3, y);
///
/// print(x); // prints "[1, 7, 8, 9, 10, 4, 5]"
/// ```
2021-12-15 05:06:17 +01:00
#[rhai_fn(name = "splice")]
pub fn splice_range(array: &mut Array, range: ExclusiveRange, replace: Array) {
let start = INT::max(range.start, 0);
let end = INT::max(range.end, start);
2022-07-27 12:04:59 +02:00
splice(array, start, end - start, replace);
2021-12-15 05:06:17 +01:00
}
/// Replace an inclusive range of the array with another array.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
/// let y = [7, 8, 9, 10];
///
/// x.splice(1..=3, y);
///
/// print(x); // prints "[1, 7, 8, 9, 10, 5]"
/// ```
2021-12-15 05:06:17 +01:00
#[rhai_fn(name = "splice")]
pub fn splice_inclusive_range(array: &mut Array, range: InclusiveRange, replace: Array) {
let start = INT::max(*range.start(), 0);
let end = INT::max(*range.end(), start);
2022-07-27 12:04:59 +02:00
splice(array, start, end - start + 1, replace);
2021-12-15 05:06:17 +01:00
}
/// Replace a portion of the array with another array.
///
/// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `start` < -length of array, position counts from the beginning of the array.
/// * If `start` ≥ length of array, the other array is appended to the end of the array.
/// * If `len` ≤ 0, the other array is inserted into the array at the `start` position without replacing any element.
/// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is replaced.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
/// let y = [7, 8, 9, 10];
///
/// x.splice(1, 2, y);
///
/// print(x); // prints "[1, 7, 8, 9, 10, 4, 5]"
///
/// x.splice(-5, 4, y);
///
/// print(x); // prints "[1, 7, 7, 8, 9, 10, 5]"
/// ```
pub fn splice(array: &mut Array, start: INT, len: INT, replace: Array) {
if array.is_empty() {
*array = replace;
return;
}
let (start, len) = calc_offset_len(array.len(), start, len);
2020-10-13 04:57:29 +02:00
if start >= array.len() {
array.extend(replace);
2020-10-13 04:57:29 +02:00
} else {
array.splice(start..start + len, replace);
}
2020-10-13 04:57:29 +02:00
}
/// Copy an exclusive range of the array and return it as a new array.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// print(x.extract(1..3)); // prints "[2, 3]"
///
/// print(x); // prints "[1, 2, 3, 4, 5]"
/// ```
2021-12-15 05:06:17 +01:00
#[rhai_fn(name = "extract")]
pub fn extract_range(array: &mut Array, range: ExclusiveRange) -> Array {
let start = INT::max(range.start, 0);
let end = INT::max(range.end, start);
extract(array, start, end - start)
}
/// Copy an inclusive range of the array and return it as a new array.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// print(x.extract(1..=3)); // prints "[2, 3, 4]"
///
/// print(x); // prints "[1, 2, 3, 4, 5]"
/// ```
2021-12-15 05:06:17 +01:00
#[rhai_fn(name = "extract")]
pub fn extract_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
let start = INT::max(*range.start(), 0);
let end = INT::max(*range.end(), start);
extract(array, start, end - start + 1)
}
/// Copy a portion of the array and return it as a new array.
///
/// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `start` < -length of array, position counts from the beginning of the array.
/// * If `start` ≥ length of array, an empty array is returned.
/// * If `len` ≤ 0, an empty array is returned.
/// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is copied and returned.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// print(x.extract(1, 3)); // prints "[2, 3, 4]"
///
/// print(x.extract(-3, 2)); // prints "[3, 4]"
///
/// print(x); // prints "[1, 2, 3, 4, 5]"
/// ```
pub fn extract(array: &mut Array, start: INT, len: INT) -> Array {
if array.is_empty() || len <= 0 {
return Array::new();
}
let (start, len) = calc_offset_len(array.len(), start, len);
2020-10-13 08:39:49 +02:00
if len == 0 {
Array::new()
} else {
array[start..start + len].to_vec()
}
2020-10-13 08:39:49 +02:00
}
/// Copy a portion of the array beginning at the `start` position till the end and return it as
/// a new array.
///
/// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `start` < -length of array, the entire array is copied and returned.
/// * If `start` ≥ length of array, an empty array is returned.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// print(x.extract(2)); // prints "[3, 4, 5]"
///
/// print(x.extract(-3)); // prints "[3, 4, 5]"
///
/// print(x); // prints "[1, 2, 3, 4, 5]"
/// ```
2020-10-13 08:39:49 +02:00
#[rhai_fn(name = "extract")]
pub fn extract_tail(array: &mut Array, start: INT) -> Array {
2021-12-12 10:26:15 +01:00
extract(array, start, INT::MAX)
2020-10-13 08:39:49 +02:00
}
/// Cut off the array at `index` and return it as a new array.
///
/// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `index` is zero, the entire array is cut and returned.
/// * If `index` < -length of array, the entire array is cut and returned.
/// * If `index` ≥ length of array, nothing is cut from the array and an empty array is returned.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.split(2);
///
/// print(y); // prints "[3, 4, 5]"
///
/// print(x); // prints "[1, 2]"
/// ```
2021-02-23 04:57:50 +01:00
#[rhai_fn(name = "split")]
pub fn split_at(array: &mut Array, index: INT) -> Array {
if array.is_empty() {
return Array::new();
}
let (start, len) = calc_offset_len(array.len(), index, INT::MAX);
if start == 0 {
if len >= array.len() {
mem::take(array)
} else {
let mut result = Array::new();
result.extend(array.drain(array.len() - len..));
result
}
} else if start >= array.len() {
Array::new()
2021-02-23 04:57:50 +01:00
} else {
2021-09-11 13:40:40 +02:00
let mut result = Array::new();
2021-02-23 04:57:50 +01:00
result.extend(array.drain(start as usize..));
result
}
}
2022-12-25 05:44:49 +01:00
/// Iterate through all the elements in the array, applying a `process` function to each element in turn.
/// Each element is bound to `this` before calling the function.
///
/// # Function Parameters
///
/// * `this`: bound to array element (mutable)
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// x.for_each(|| this *= this);
///
/// print(x); // prints "[1, 4, 9, 16, 25]"
///
/// x.for_each(|i| this *= i);
///
/// print(x); // prints "[0, 2, 6, 12, 20]"
/// ```
#[rhai_fn(return_raw)]
pub fn for_each(ctx: NativeCallContext, array: &mut Array, map: FnPtr) -> RhaiResultOf<()> {
if array.is_empty() {
return Ok(());
}
for (i, item) in array.iter_mut().enumerate() {
let ex = [(i as INT).into()];
let _ = map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex, None)?;
}
Ok(())
}
/// Iterate through all the elements in the array, applying a `mapper` function to each element
/// in turn, and return the results as a new array.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `mapper` function should not mutate array elements.
///
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.map(|v| v * v);
///
/// print(y); // prints "[1, 4, 9, 16, 25]"
///
/// let y = x.map(|v, i| v * i);
///
/// print(y); // prints "[0, 2, 6, 12, 20]"
/// ```
2023-01-03 07:00:18 +01:00
#[rhai_fn(return_raw, pure)]
pub fn map(ctx: NativeCallContext, array: &mut Array, map: FnPtr) -> RhaiResultOf<Array> {
if array.is_empty() {
return Ok(Array::new());
}
2021-03-23 05:13:53 +01:00
let mut ar = Array::with_capacity(array.len());
for (i, item) in array.iter_mut().enumerate() {
2022-12-20 07:43:30 +01:00
let ex = [(i as INT).into()];
2022-12-25 05:44:49 +01:00
ar.push(map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex, Some(0))?);
}
2020-10-18 07:18:12 +02:00
2021-03-22 04:18:09 +01:00
Ok(ar)
2020-10-18 07:18:12 +02:00
}
2021-11-23 15:37:18 +01:00
/// Iterate through all the elements in the array, applying a `filter` function to each element
/// in turn, and return a copy of all elements (in order) that return `true` as a new array.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `filter` function should not mutate array elements.
///
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.filter(|v| v >= 3);
///
/// print(y); // prints "[3, 4, 5]"
///
/// let y = x.filter(|v, i| v * i >= 10);
///
/// print(y); // prints "[12, 20]"
/// ```
2023-01-03 07:00:18 +01:00
#[rhai_fn(return_raw, pure)]
pub fn filter(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
if array.is_empty() {
return Ok(Array::new());
}
2021-03-23 05:13:53 +01:00
let mut ar = Array::new();
for (i, item) in array.iter_mut().enumerate() {
2022-12-20 07:43:30 +01:00
let ex = [(i as INT).into()];
if filter
2022-12-25 05:44:49 +01:00
.call_raw_with_extra_args("filter", &ctx, Some(item), [], ex, Some(0))?
2021-11-23 15:37:18 +01:00
.as_bool()
.unwrap_or(false)
{
ar.push(item.clone());
}
}
2020-08-14 12:58:34 +02:00
2021-03-22 04:18:09 +01:00
Ok(ar)
}
/// Return `true` if the array contains an element that equals `value`.
///
/// The operator `==` is used to compare elements with `value` and must be defined,
/// otherwise `false` is assumed.
///
/// This function also drives the `in` operator.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// // The 'in' operator calls 'contains' in the background
/// if 4 in x {
/// print("found!");
/// }
/// ```
2021-03-09 06:16:05 +01:00
#[rhai_fn(return_raw, pure)]
pub fn contains(
ctx: NativeCallContext,
array: &mut Array,
value: Dynamic,
2021-12-25 16:49:14 +01:00
) -> RhaiResultOf<bool> {
if array.is_empty() {
return Ok(false);
}
2022-09-26 17:35:37 +02:00
for item in array {
2021-03-09 06:16:05 +01:00
if ctx
2022-10-14 10:57:14 +02:00
.call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
2021-03-09 06:16:05 +01:00
.or_else(|err| match *err {
2022-02-08 02:02:15 +01:00
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
2021-03-09 06:16:05 +01:00
if item.type_id() == value.type_id() {
// No default when comparing same type
Err(err)
} else {
Ok(Dynamic::FALSE)
}
}
_ => Err(err),
})?
.as_bool()
.unwrap_or(false)
{
2021-03-22 04:18:09 +01:00
return Ok(true);
2021-03-09 06:16:05 +01:00
}
}
2021-03-22 04:18:09 +01:00
Ok(false)
2021-03-09 06:16:05 +01:00
}
/// Find the first element in the array that equals a particular `value` and return its index.
/// If no element equals `value`, `-1` is returned.
///
/// The operator `==` is used to compare elements with `value` and must be defined,
/// otherwise `false` is assumed.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
///
/// print(x.index_of(4)); // prints 3 (first index)
///
/// print(x.index_of(9)); // prints -1
///
/// print(x.index_of("foo")); // prints -1: strings do not equal numbers
/// ```
2021-03-09 06:16:05 +01:00
#[rhai_fn(return_raw, pure)]
2020-11-08 16:00:37 +01:00
pub fn index_of(
2021-03-09 06:16:05 +01:00
ctx: NativeCallContext,
array: &mut Array,
value: Dynamic,
2021-12-25 16:49:14 +01:00
) -> RhaiResultOf<INT> {
if array.is_empty() {
Ok(-1)
} else {
index_of_starting_from(ctx, array, value, 0)
}
}
/// Find the first element in the array, starting from a particular `start` position, that
/// equals a particular `value` and return its index. If no element equals `value`, `-1` is returned.
///
/// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `start` < -length of array, position counts from the beginning of the array.
/// * If `start` ≥ length of array, `-1` is returned.
///
/// The operator `==` is used to compare elements with `value` and must be defined,
/// otherwise `false` is assumed.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
///
/// print(x.index_of(4, 2)); // prints 3
///
/// print(x.index_of(4, 5)); // prints 7
///
/// print(x.index_of(4, 15)); // prints -1: nothing found past end of array
///
/// print(x.index_of(4, -5)); // prints 11: -5 = start from index 8
///
/// print(x.index_of(9, 1)); // prints -1: nothing equals 9
///
/// print(x.index_of("foo", 1)); // prints -1: strings do not equal numbers
/// ```
#[rhai_fn(name = "index_of", return_raw, pure)]
pub fn index_of_starting_from(
ctx: NativeCallContext,
array: &mut Array,
value: Dynamic,
start: INT,
2021-12-25 16:49:14 +01:00
) -> RhaiResultOf<INT> {
if array.is_empty() {
return Ok(-1);
}
2022-02-08 02:02:15 +01:00
let (start, ..) = calc_offset_len(array.len(), start, 0);
for (i, item) in array.iter_mut().enumerate().skip(start) {
2021-03-09 06:16:05 +01:00
if ctx
2022-10-14 10:57:14 +02:00
.call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
2021-03-09 06:16:05 +01:00
.or_else(|err| match *err {
2022-02-08 02:02:15 +01:00
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
2021-03-09 06:16:05 +01:00
if item.type_id() == value.type_id() {
// No default when comparing same type
Err(err)
} else {
Ok(Dynamic::FALSE)
}
}
_ => Err(err),
})?
.as_bool()
.unwrap_or(false)
{
2021-03-22 04:18:09 +01:00
return Ok(i as INT);
2021-03-09 06:16:05 +01:00
}
}
2021-03-22 04:18:09 +01:00
Ok(-1 as INT)
2021-03-09 06:16:05 +01:00
}
/// Iterate through all the elements in the array, applying a `filter` function to each element
/// in turn, and return the index of the first element that returns `true`.
/// If no element returns `true`, `-1` is returned.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `filter` function should not mutate array elements.
///
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
///
/// print(x.index_of(|v| v > 3)); // prints 3: 4 > 3
///
/// print(x.index_of(|v| v > 8)); // prints -1: nothing is > 8
///
/// print(x.index_of(|v, i| v * i > 20)); // prints 7: 4 * 7 > 20
/// ```
#[rhai_fn(name = "index_of", return_raw, pure)]
2021-03-09 06:16:05 +01:00
pub fn index_of_filter(
2020-11-08 16:00:37 +01:00
ctx: NativeCallContext,
array: &mut Array,
2020-11-08 16:00:37 +01:00
filter: FnPtr,
2021-12-25 16:49:14 +01:00
) -> RhaiResultOf<INT> {
if array.is_empty() {
Ok(-1)
} else {
index_of_filter_starting_from(ctx, array, filter, 0)
}
}
/// Iterate through all the elements in the array, starting from a particular `start` position,
/// applying a `filter` function to each element in turn, and return the index of the first
/// element that returns `true`. If no element returns `true`, `-1` is returned.
///
/// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `start` < -length of array, position counts from the beginning of the array.
/// * If `start` ≥ length of array, `-1` is returned.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `filter` function should not mutate array elements.
///
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
///
/// print(x.index_of(|v| v > 1, 3)); // prints 5: 2 > 1
///
/// print(x.index_of(|v| v < 2, 9)); // prints -1: nothing < 2 past index 9
///
/// print(x.index_of(|v| v > 1, 15)); // prints -1: nothing found past end of array
///
/// print(x.index_of(|v| v > 1, -5)); // prints 9: -5 = start from index 8
///
/// print(x.index_of(|v| v > 1, -99)); // prints 1: -99 = start from beginning
///
/// print(x.index_of(|v, i| v * i > 20, 8)); // prints 10: 3 * 10 > 20
/// ```
#[rhai_fn(name = "index_of", return_raw, pure)]
2021-10-21 13:04:53 +02:00
pub fn index_of_filter_starting_from(
ctx: NativeCallContext,
array: &mut Array,
2021-10-21 13:04:53 +02:00
filter: FnPtr,
start: INT,
2021-12-25 16:49:14 +01:00
) -> RhaiResultOf<INT> {
if array.is_empty() {
return Ok(-1);
}
2022-02-08 02:02:15 +01:00
let (start, ..) = calc_offset_len(array.len(), start, 0);
for (i, item) in array.iter_mut().enumerate().skip(start) {
2022-12-20 07:43:30 +01:00
let ex = [(i as INT).into()];
if filter
2022-12-25 05:44:49 +01:00
.call_raw_with_extra_args("index_of", &ctx, Some(item), [], ex, Some(0))?
2021-11-23 15:37:18 +01:00
.as_bool()
.unwrap_or(false)
{
2021-03-22 04:18:09 +01:00
return Ok(i as INT);
2020-11-08 16:00:37 +01:00
}
}
2021-03-22 04:18:09 +01:00
Ok(-1 as INT)
2020-11-08 16:00:37 +01:00
}
2022-12-20 15:27:29 +01:00
/// Iterate through all the elements in the array, applying a `filter` function to each element
/// in turn, and return a copy of the first element that returns `true`. If no element returns
/// `true`, `()` is returned.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
2022-12-20 15:27:29 +01:00
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 5, 8, 13];
///
/// print(x.find(|v| v > 3)); // prints 5: 5 > 3
///
/// print(x.find(|v| v > 13) ?? "not found"); // prints "not found": nothing is > 13
2022-12-20 15:27:29 +01:00
///
/// print(x.find(|v, i| v * i > 13)); // prints 5: 3 * 5 > 13
/// ```
#[rhai_fn(return_raw, pure)]
pub fn find(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResult {
find_starting_from(ctx, array, filter, 0)
}
/// Iterate through all the elements in the array, starting from a particular `start` position,
/// applying a `filter` function to each element in turn, and return a copy of the first element
/// that returns `true`. If no element returns `true`, `()` is returned.
///
/// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `start` < -length of array, position counts from the beginning of the array.
/// * If `start` ≥ length of array, `-1` is returned.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `filter` function should not mutate array elements.
///
2022-12-20 15:27:29 +01:00
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 5, 8, 13];
///
/// print(x.find(|v| v > 1, 2)); // prints 3: 3 > 1
///
/// print(x.find(|v| v < 2, 3) ?? "not found"); // prints "not found": nothing < 2 past index 3
2022-12-20 15:27:29 +01:00
///
/// print(x.find(|v| v > 1, 8) ?? "not found"); // prints "not found": nothing found past end of array
2022-12-20 15:27:29 +01:00
///
/// print(x.find(|v| v > 1, -3)); // prints 5: -3 = start from index 4
///
/// print(x.find(|v| v > 0, -99)); // prints 1: -99 = start from beginning
///
/// print(x.find(|v, i| v * i > 6, 3)); // prints 5: 5 * 4 > 6
/// ```
#[rhai_fn(name = "find", return_raw, pure)]
pub fn find_starting_from(
ctx: NativeCallContext,
array: &mut Array,
filter: FnPtr,
start: INT,
) -> RhaiResult {
let index = index_of_filter_starting_from(ctx, array, filter, start)?;
if index < 0 {
return Ok(Dynamic::UNIT);
}
Ok(get(array, index))
}
/// Iterate through all the elements in the array, applying a `mapper` function to each element
/// in turn, and return the first result that is not `()`. Otherwise, `()` is returned.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `mapper` function should not mutate array elements.
///
2022-12-20 15:27:29 +01:00
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [#{alice: 1}, #{bob: 2}, #{clara: 3}];
///
/// print(x.find_map(|v| v.alice)); // prints 1
///
/// print(x.find_map(|v| v.dave) ?? "not found"); // prints "not found"
///
/// print(x.find_map(|| this.dave) ?? "not found"); // prints "not found"
2022-12-20 15:27:29 +01:00
/// ```
#[rhai_fn(return_raw, pure)]
pub fn find_map(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResult {
find_map_starting_from(ctx, array, filter, 0)
}
/// Iterate through all the elements in the array, starting from a particular `start` position,
/// applying a `mapper` function to each element in turn, and return the first result that is not `()`.
/// Otherwise, `()` is returned.
///
/// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `start` < -length of array, position counts from the beginning of the array.
/// * If `start` ≥ length of array, `-1` is returned.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `mapper` function should not mutate array elements.
///
2022-12-20 15:27:29 +01:00
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [#{alice: 1}, #{bob: 2}, #{bob: 3}, #{clara: 3}, #{alice: 0}, #{clara: 5}];
///
/// print(x.find_map(|v| v.alice, 2)); // prints 0
///
/// print(x.find_map(|v| v.bob, 4) ?? "not found"); // prints "not found"
2022-12-20 15:27:29 +01:00
///
/// print(x.find_map(|v| v.alice, 8) ?? "not found"); // prints "not found"
///
/// print(x.find_map(|| this.alice, 8) ?? "not found"); // prints "not found"
2022-12-20 15:27:29 +01:00
///
/// print(x.find_map(|v| v.bob, -4)); // prints 3: -4 = start from index 2
///
/// print(x.find_map(|v| v.alice, -99)); // prints 1: -99 = start from beginning
///
/// print(x.find_map(|| this.alice, -99)); // prints 1: -99 = start from beginning
2022-12-20 15:27:29 +01:00
/// ```
#[rhai_fn(name = "find_map", return_raw, pure)]
pub fn find_map_starting_from(
ctx: NativeCallContext,
array: &mut Array,
filter: FnPtr,
start: INT,
) -> RhaiResult {
if array.is_empty() {
return Ok(Dynamic::UNIT);
}
let (start, ..) = calc_offset_len(array.len(), start, 0);
for (i, item) in array.iter_mut().enumerate().skip(start) {
2022-12-20 15:27:29 +01:00
let ex = [(i as INT).into()];
2022-12-25 05:44:49 +01:00
let value =
filter.call_raw_with_extra_args("find_map", &ctx, Some(item), [], ex, Some(0))?;
2022-12-20 15:27:29 +01:00
if !value.is_unit() {
return Ok(value);
}
}
Ok(Dynamic::UNIT)
}
/// Return `true` if any element in the array that returns `true` when applied the `filter` function.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `filter` function should not mutate array elements.
///
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
///
/// print(x.some(|v| v > 3)); // prints true
///
/// print(x.some(|v| v > 10)); // prints false
///
/// print(x.some(|v, i| i > v)); // prints true
/// ```
2021-11-23 15:37:18 +01:00
#[rhai_fn(return_raw, pure)]
2021-12-25 16:49:14 +01:00
pub fn some(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<bool> {
if array.is_empty() {
return Ok(false);
}
for (i, item) in array.iter_mut().enumerate() {
2022-12-20 07:43:30 +01:00
let ex = [(i as INT).into()];
if filter
2022-12-25 05:44:49 +01:00
.call_raw_with_extra_args("some", &ctx, Some(item), [], ex, Some(0))?
2021-11-23 15:37:18 +01:00
.as_bool()
.unwrap_or(false)
{
2021-03-22 04:18:09 +01:00
return Ok(true);
}
2020-06-30 12:34:32 +02:00
}
2020-10-12 16:49:51 +02:00
2021-03-22 04:18:09 +01:00
Ok(false)
}
/// Return `true` if all elements in the array return `true` when applied the `filter` function.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `filter` function should not mutate array elements.
///
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
///
/// print(x.all(|v| v > 3)); // prints false
///
/// print(x.all(|v| v > 1)); // prints true
///
/// print(x.all(|v, i| i > v)); // prints false
/// ```
2021-11-23 15:37:18 +01:00
#[rhai_fn(return_raw, pure)]
2021-12-25 16:49:14 +01:00
pub fn all(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<bool> {
if array.is_empty() {
return Ok(true);
}
for (i, item) in array.iter_mut().enumerate() {
2022-12-20 07:43:30 +01:00
let ex = [(i as INT).into()];
if !filter
2022-12-25 05:44:49 +01:00
.call_raw_with_extra_args("all", &ctx, Some(item), [], ex, Some(0))?
2021-11-23 15:37:18 +01:00
.as_bool()
.unwrap_or(false)
{
2021-03-22 04:18:09 +01:00
return Ok(false);
}
2020-10-12 16:49:51 +02:00
}
2021-03-22 04:18:09 +01:00
Ok(true)
2020-10-13 04:57:29 +02:00
}
/// Remove duplicated _consecutive_ elements from the array.
///
/// The operator `==` is used to compare elements and must be defined,
/// otherwise `false` is assumed.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 2, 2, 3, 4, 3, 3, 2, 1];
///
/// x.dedup();
///
/// print(x); // prints "[1, 2, 3, 4, 3, 2, 1]"
/// ```
2022-08-29 08:27:05 +02:00
pub fn dedup(ctx: NativeCallContext, array: &mut Array) {
let comparer = FnPtr::new_unchecked(OP_EQUALS, StaticVec::new_const());
2022-12-22 10:34:58 +01:00
dedup_by_comparer(ctx, array, comparer);
2021-10-21 11:59:54 +02:00
}
/// Remove duplicated _consecutive_ elements from the array that return `true` when applied the
/// `comparer` function.
///
/// No element is removed if the correct `comparer` function does not exist.
///
/// # Function Parameters
///
/// * `element1`: copy of the current array element to compare
/// * `element2`: copy of the next array element to compare
///
/// ## Return Value
///
/// `true` if `element1 == element2`, otherwise `false`.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 2, 2, 3, 1, 2, 3, 4, 3, 3, 2, 1];
///
/// x.dedup(|a, b| a >= b);
///
/// print(x); // prints "[1, 2, 3, 4]"
/// ```
2022-08-29 08:27:05 +02:00
#[rhai_fn(name = "dedup")]
pub fn dedup_by_comparer(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) {
2021-10-21 11:59:54 +02:00
if array.is_empty() {
2022-08-29 08:27:05 +02:00
return;
2021-10-21 11:59:54 +02:00
}
array.dedup_by(|x, y| {
2021-11-23 15:37:18 +01:00
comparer
.call_raw(&ctx, None, [y.clone(), x.clone()])
2022-07-27 10:04:24 +02:00
.unwrap_or(Dynamic::FALSE)
2021-10-21 11:59:54 +02:00
.as_bool()
.unwrap_or(false)
});
}
/// Reduce an array by iterating through all elements while applying the `reducer` function.
///
/// # Function Parameters
///
/// * `result`: accumulated result, initially `()`
2023-01-03 07:00:18 +01:00
/// * `element`: copy of array element, or bound to `this` if omitted
/// * `index` _(optional)_: current index in the array
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `reducer` function should not mutate array elements.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
2022-06-11 10:01:15 +02:00
/// let y = x.reduce(|r, v| v + (r ?? 0));
///
/// print(y); // prints 15
///
2022-06-11 10:01:15 +02:00
/// let y = x.reduce(|r, v, i| v + i + (r ?? 0));
///
/// print(y); // prints 25
/// ```
2021-03-09 06:16:05 +01:00
#[rhai_fn(return_raw, pure)]
2021-12-25 16:49:14 +01:00
pub fn reduce(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult {
2021-11-23 15:37:18 +01:00
reduce_with_initial(ctx, array, reducer, Dynamic::UNIT)
2020-10-12 16:49:51 +02:00
}
/// Reduce an array by iterating through all elements while applying the `reducer` function.
///
/// # Function Parameters
///
/// * `result`: accumulated result, starting with the value of `initial`
2023-01-03 07:00:18 +01:00
/// * `element`: copy of array element, or bound to `this` if omitted
/// * `index` _(optional)_: current index in the array
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `reducer` function should not mutate array elements.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.reduce(|r, v| v + r, 5);
///
/// print(y); // prints 20
///
/// let y = x.reduce(|r, v, i| v + i + r, 5);
///
/// print(y); // prints 30
/// ```
#[rhai_fn(name = "reduce", return_raw, pure)]
pub fn reduce_with_initial(
2020-11-02 04:04:45 +01:00
ctx: NativeCallContext,
array: &mut Array,
reducer: FnPtr,
2021-03-22 04:18:09 +01:00
initial: Dynamic,
2021-12-25 16:49:14 +01:00
) -> RhaiResult {
if array.is_empty() {
return Ok(initial);
}
array
2022-12-25 05:44:49 +01:00
.iter_mut()
.enumerate()
.try_fold(initial, |result, (i, item)| {
2022-12-20 07:43:30 +01:00
let ex = [(i as INT).into()];
2022-12-25 05:44:49 +01:00
reducer.call_raw_with_extra_args("reduce", &ctx, Some(item), [result], ex, Some(1))
})
2020-10-14 15:27:31 +02:00
}
/// Reduce an array by iterating through all elements, in _reverse_ order,
/// while applying the `reducer` function.
///
/// # Function Parameters
///
/// * `result`: accumulated result, initially `()`
2023-01-03 07:00:18 +01:00
/// * `element`: copy of array element, or bound to `this` if omitted
/// * `index` _(optional)_: current index in the array
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `reducer` function should not mutate array elements.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
2022-06-11 10:01:15 +02:00
/// let y = x.reduce_rev(|r, v| v + (r ?? 0));
///
/// print(y); // prints 15
///
2022-06-11 10:01:15 +02:00
/// let y = x.reduce_rev(|r, v, i| v + i + (r ?? 0));
///
/// print(y); // prints 25
/// ```
2021-03-09 06:16:05 +01:00
#[rhai_fn(return_raw, pure)]
2021-12-25 16:49:14 +01:00
pub fn reduce_rev(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult {
2021-11-23 15:37:18 +01:00
reduce_rev_with_initial(ctx, array, reducer, Dynamic::UNIT)
2020-10-14 15:27:31 +02:00
}
/// Reduce an array by iterating through all elements, in _reverse_ order,
/// while applying the `reducer` function.
///
/// # Function Parameters
///
/// * `result`: accumulated result, starting with the value of `initial`
2023-01-03 07:00:18 +01:00
/// * `element`: copy of array element, or bound to `this` if omitted
/// * `index` _(optional)_: current index in the array
///
2023-01-03 07:00:18 +01:00
/// This method is marked _pure_; the `reducer` function should not mutate array elements.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.reduce_rev(|r, v| v + r, 5);
///
/// print(y); // prints 20
///
/// let y = x.reduce_rev(|r, v, i| v + i + r, 5);
///
/// print(y); // prints 30
/// ```
2021-03-09 06:16:05 +01:00
#[rhai_fn(name = "reduce_rev", return_raw, pure)]
pub fn reduce_rev_with_initial(
2020-11-02 04:04:45 +01:00
ctx: NativeCallContext,
array: &mut Array,
reducer: FnPtr,
2021-03-22 04:18:09 +01:00
initial: Dynamic,
2021-12-25 16:49:14 +01:00
) -> RhaiResult {
if array.is_empty() {
return Ok(initial);
}
2022-12-25 05:44:49 +01:00
let len = array.len();
array
2022-12-25 05:44:49 +01:00
.iter_mut()
.rev()
.enumerate()
.try_fold(initial, |result, (i, item)| {
2022-12-25 05:44:49 +01:00
let ex = [((len - 1 - i) as INT).into()];
2022-12-20 07:43:30 +01:00
reducer.call_raw_with_extra_args(
2022-12-11 10:18:14 +01:00
"reduce_rev",
&ctx,
2022-12-25 05:44:49 +01:00
Some(item),
[result],
2022-12-20 07:43:30 +01:00
ex,
2022-12-25 05:44:49 +01:00
Some(1),
2022-12-11 10:18:14 +01:00
)
})
2020-10-13 04:57:29 +02:00
}
/// Sort the array based on applying the `comparer` function.
///
/// # Function Parameters
///
/// * `element1`: copy of the current array element to compare
/// * `element2`: copy of the next array element to compare
///
/// ## Return Value
///
/// * Any integer > 0 if `element1 > element2`
/// * Zero if `element1 == element2`
/// * Any integer < 0 if `element1 < element2`
///
/// # Example
///
/// ```rhai
/// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10];
///
/// // Do comparisons in reverse
/// x.sort(|a, b| if a > b { -1 } else if a < b { 1 } else { 0 });
///
/// print(x); // prints "[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]"
/// ```
2023-02-10 16:51:22 +01:00
pub fn sort(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) {
2021-10-23 05:51:48 +02:00
if array.len() <= 1 {
2023-02-10 16:51:22 +01:00
return;
}
array.sort_by(|x, y| {
comparer
2021-11-29 05:43:59 +01:00
.call_raw(&ctx, None, [x.clone(), y.clone()])
.ok()
.and_then(|v| v.as_int().ok())
2022-07-27 12:04:59 +02:00
.map_or_else(
|| x.type_id().cmp(&y.type_id()),
|v| match v {
v if v > 0 => Ordering::Greater,
v if v < 0 => Ordering::Less,
0 => Ordering::Equal,
_ => unreachable!("v is {}", v),
},
)
});
}
/// Sort the array.
///
/// All elements in the array must be of the same data type.
///
/// # Supported Data Types
///
/// * integer numbers
/// * floating-point numbers
/// * decimal numbers
/// * characters
/// * strings
/// * booleans
/// * `()`
///
/// # Example
///
/// ```rhai
/// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10];
///
/// x.sort();
///
/// print(x); // prints "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
/// ```
2021-10-23 05:51:48 +02:00
#[rhai_fn(name = "sort", return_raw)]
2021-12-25 16:49:14 +01:00
pub fn sort_with_builtin(array: &mut Array) -> RhaiResultOf<()> {
2021-10-23 05:51:48 +02:00
if array.len() <= 1 {
return Ok(());
}
let type_id = array[0].type_id();
if array.iter().any(|a| a.type_id() != type_id) {
2021-12-27 05:27:31 +01:00
return Err(ERR::ErrorFunctionNotFound(
2021-10-23 05:51:48 +02:00
"sort() cannot be called with elements of different types".into(),
Position::NONE,
)
.into());
}
if type_id == TypeId::of::<INT>() {
array.sort_by(|a, b| {
2021-11-13 05:23:35 +01:00
let a = a.as_int().expect("`INT`");
let b = b.as_int().expect("`INT`");
2021-10-23 05:51:48 +02:00
a.cmp(&b)
});
return Ok(());
}
if type_id == TypeId::of::<char>() {
array.sort_by(|a, b| {
2021-11-13 05:23:35 +01:00
let a = a.as_char().expect("char");
let b = b.as_char().expect("char");
2021-10-23 05:51:48 +02:00
a.cmp(&b)
});
return Ok(());
}
#[cfg(not(feature = "no_float"))]
if type_id == TypeId::of::<crate::FLOAT>() {
array.sort_by(|a, b| {
2021-11-13 05:23:35 +01:00
let a = a.as_float().expect("`FLOAT`");
let b = b.as_float().expect("`FLOAT`");
2021-10-23 05:51:48 +02:00
a.partial_cmp(&b).unwrap_or(Ordering::Equal)
});
return Ok(());
}
if type_id == TypeId::of::<ImmutableString>() {
array.sort_by(|a, b| {
2021-11-13 05:23:35 +01:00
let a = a.read_lock::<ImmutableString>().expect("`ImmutableString`");
let b = b.read_lock::<ImmutableString>().expect("`ImmutableString`");
2021-10-23 05:51:48 +02:00
a.as_str().cmp(b.as_str())
});
return Ok(());
}
#[cfg(feature = "decimal")]
if type_id == TypeId::of::<rust_decimal::Decimal>() {
array.sort_by(|a, b| {
2021-11-13 05:23:35 +01:00
let a = a.as_decimal().expect("`Decimal`");
let b = b.as_decimal().expect("`Decimal`");
2021-10-23 05:51:48 +02:00
a.cmp(&b)
});
return Ok(());
}
if type_id == TypeId::of::<bool>() {
array.sort_by(|a, b| {
2021-11-13 05:23:35 +01:00
let a = a.as_bool().expect("`bool`");
let b = b.as_bool().expect("`bool`");
2021-10-23 05:51:48 +02:00
a.cmp(&b)
});
return Ok(());
}
if type_id == TypeId::of::<()>() {
return Ok(());
}
Ok(())
}
/// Remove all elements in the array that returns `true` when applied the `filter` function and
/// return them as a new array.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.drain(|v| v < 3);
///
/// print(x); // prints "[3, 4, 5]"
///
/// print(y); // prints "[1, 2]"
///
/// let z = x.drain(|v, i| v + i > 5);
///
/// print(x); // prints "[3, 4]"
///
/// print(z); // prints "[5]"
/// ```
2021-11-23 15:37:18 +01:00
#[rhai_fn(return_raw)]
2021-12-27 04:43:11 +01:00
pub fn drain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
if array.is_empty() {
return Ok(Array::new());
}
2021-11-23 15:37:18 +01:00
let mut drained = Array::with_capacity(array.len());
2020-10-13 04:57:29 +02:00
2021-11-23 15:37:18 +01:00
let mut i = 0;
let mut x = 0;
2020-10-18 07:18:12 +02:00
2021-11-23 15:37:18 +01:00
while x < array.len() {
2022-12-20 07:43:30 +01:00
let ex = [(i as INT).into()];
if filter
2022-12-25 05:44:49 +01:00
.call_raw_with_extra_args("drain", &ctx, Some(&mut array[x]), [], ex, Some(0))?
2021-11-23 15:37:18 +01:00
.as_bool()
.unwrap_or(false)
{
drained.push(array.remove(x));
} else {
2021-11-23 15:37:18 +01:00
x += 1;
}
2021-11-23 15:37:18 +01:00
i += 1;
}
2021-11-23 15:37:18 +01:00
Ok(drained)
}
/// Remove all elements in the array within an exclusive `range` and return them as a new array.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.drain(1..3);
///
/// print(x); // prints "[1, 4, 5]"
///
/// print(y); // prints "[2, 3]"
///
/// let z = x.drain(2..3);
///
/// print(x); // prints "[1, 4]"
///
/// print(z); // prints "[5]"
/// ```
#[rhai_fn(name = "drain")]
2021-12-15 05:06:17 +01:00
pub fn drain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array {
let start = INT::max(range.start, 0);
let end = INT::max(range.end, start);
drain_range(array, start, end - start)
}
/// Remove all elements in the array within an inclusive `range` and return them as a new array.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.drain(1..=2);
///
/// print(x); // prints "[1, 4, 5]"
///
/// print(y); // prints "[2, 3]"
///
/// let z = x.drain(2..=2);
///
/// print(x); // prints "[1, 4]"
///
/// print(z); // prints "[5]"
/// ```
2021-12-15 05:06:17 +01:00
#[rhai_fn(name = "drain")]
pub fn drain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
let start = INT::max(*range.start(), 0);
let end = INT::max(*range.end(), start);
drain_range(array, start, end - start + 1)
}
/// Remove all elements within a portion of the array and return them as a new array.
///
/// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `start` < -length of array, position counts from the beginning of the array.
/// * If `start` ≥ length of array, no element is removed and an empty array is returned.
/// * If `len` ≤ 0, no element is removed and an empty array is returned.
/// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is removed and returned.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.drain(1, 2);
///
/// print(x); // prints "[1, 4, 5]"
///
/// print(y); // prints "[2, 3]"
///
/// let z = x.drain(-1, 1);
///
/// print(x); // prints "[1, 4]"
///
/// print(z); // prints "[5]"
/// ```
2021-12-15 05:06:17 +01:00
#[rhai_fn(name = "drain")]
pub fn drain_range(array: &mut Array, start: INT, len: INT) -> Array {
if array.is_empty() || len <= 0 {
return Array::new();
}
let (start, len) = calc_offset_len(array.len(), start, len);
2020-10-18 07:18:12 +02:00
if len == 0 {
Array::new()
} else {
array.drain(start..start + len).collect()
}
2020-10-18 07:18:12 +02:00
}
/// Remove all elements in the array that do not return `true` when applied the `filter`
/// function and return them as a new array.
///
/// # No Function Parameter
///
/// Array element (mutable) is bound to `this`.
///
/// # Function Parameters
///
/// * `element`: copy of array element
/// * `index` _(optional)_: current index in the array
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.retain(|v| v >= 3);
///
/// print(x); // prints "[3, 4, 5]"
///
/// print(y); // prints "[1, 2]"
///
/// let z = x.retain(|v, i| v + i <= 5);
///
/// print(x); // prints "[3, 4]"
///
/// print(z); // prints "[5]"
/// ```
2021-11-23 15:37:18 +01:00
#[rhai_fn(return_raw)]
2021-12-27 04:43:11 +01:00
pub fn retain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
if array.is_empty() {
return Ok(Array::new());
}
2021-11-23 15:37:18 +01:00
let mut drained = Array::new();
2020-10-18 07:18:12 +02:00
2021-11-23 15:37:18 +01:00
let mut i = 0;
let mut x = 0;
2020-10-18 07:18:12 +02:00
2021-11-23 15:37:18 +01:00
while x < array.len() {
2022-12-20 07:43:30 +01:00
let ex = [(i as INT).into()];
if filter
2022-12-25 05:44:49 +01:00
.call_raw_with_extra_args("retain", &ctx, Some(&mut array[x]), [], ex, Some(0))?
2021-11-23 15:37:18 +01:00
.as_bool()
.unwrap_or(false)
{
x += 1;
2022-07-27 12:04:59 +02:00
} else {
drained.push(array.remove(x));
}
2021-11-23 15:37:18 +01:00
i += 1;
}
2021-11-23 15:37:18 +01:00
Ok(drained)
}
/// Remove all elements in the array not within an exclusive `range` and return them as a new array.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.retain(1..4);
///
/// print(x); // prints "[2, 3, 4]"
///
/// print(y); // prints "[1, 5]"
///
/// let z = x.retain(1..3);
///
/// print(x); // prints "[3, 4]"
///
/// print(z); // prints "[1]"
/// ```
#[rhai_fn(name = "retain")]
2021-12-15 05:06:17 +01:00
pub fn retain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array {
let start = INT::max(range.start, 0);
let end = INT::max(range.end, start);
retain_range(array, start, end - start)
}
/// Remove all elements in the array not within an inclusive `range` and return them as a new array.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.retain(1..=3);
///
/// print(x); // prints "[2, 3, 4]"
///
/// print(y); // prints "[1, 5]"
///
/// let z = x.retain(1..=2);
///
/// print(x); // prints "[3, 4]"
///
/// print(z); // prints "[1]"
/// ```
2021-12-15 05:06:17 +01:00
#[rhai_fn(name = "retain")]
pub fn retain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
let start = INT::max(*range.start(), 0);
let end = INT::max(*range.end(), start);
retain_range(array, start, end - start + 1)
}
/// Remove all elements not within a portion of the array and return them as a new array.
///
/// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
/// * If `start` < -length of array, position counts from the beginning of the array.
/// * If `start` ≥ length of array, all elements are removed returned.
/// * If `len` ≤ 0, all elements are removed and returned.
/// * If `start` position + `len` ≥ length of array, entire portion of the array before the `start` position is removed and returned.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
///
/// let y = x.retain(1, 2);
///
/// print(x); // prints "[2, 3]"
///
/// print(y); // prints "[1, 4, 5]"
///
/// let z = x.retain(-1, 1);
///
/// print(x); // prints "[3]"
///
/// print(z); // prints "[2]"
/// ```
2021-12-15 05:06:17 +01:00
#[rhai_fn(name = "retain")]
pub fn retain_range(array: &mut Array, start: INT, len: INT) -> Array {
if array.is_empty() || len <= 0 {
return Array::new();
}
let (start, len) = calc_offset_len(array.len(), start, len);
2020-10-18 07:18:12 +02:00
if len == 0 {
Array::new()
} else {
let mut drained: Array = array.drain(..start).collect();
drained.extend(array.drain(len..));
2020-10-18 07:18:12 +02:00
drained
}
2020-10-18 07:18:12 +02:00
}
/// Return `true` if two arrays are equal (i.e. all elements are equal and in the same order).
///
/// The operator `==` is used to compare elements and must be defined,
/// otherwise `false` is assumed.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
/// let y = [1, 2, 3, 4, 5];
/// let z = [1, 2, 3, 4];
///
/// print(x == y); // prints true
///
/// print(x == z); // prints false
/// ```
2021-03-09 06:16:05 +01:00
#[rhai_fn(name = "==", return_raw, pure)]
2021-12-27 04:43:11 +01:00
pub fn equals(ctx: NativeCallContext, array1: &mut Array, array2: Array) -> RhaiResultOf<bool> {
2021-08-13 07:42:39 +02:00
if array1.len() != array2.len() {
2021-03-22 04:18:09 +01:00
return Ok(false);
2020-11-08 16:00:37 +01:00
}
2021-08-13 07:42:39 +02:00
if array1.is_empty() {
2021-03-22 04:18:09 +01:00
return Ok(true);
2020-11-08 16:00:37 +01:00
}
2021-08-13 07:42:39 +02:00
let mut array2 = array2;
for (a1, a2) in array1.iter_mut().zip(array2.iter_mut()) {
2021-03-09 06:16:05 +01:00
if !ctx
2022-10-14 10:57:14 +02:00
.call_native_fn_raw(OP_EQUALS, true, &mut [a1, a2])
2021-03-09 06:16:05 +01:00
.or_else(|err| match *err {
2022-02-08 02:02:15 +01:00
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
2021-03-09 06:16:05 +01:00
if a1.type_id() == a2.type_id() {
// No default when comparing same type
Err(err)
} else {
Ok(Dynamic::FALSE)
}
}
_ => Err(err),
})?
.as_bool()
.unwrap_or(false)
{
2021-03-22 04:18:09 +01:00
return Ok(false);
2020-11-08 16:00:37 +01:00
}
}
2021-03-22 04:18:09 +01:00
Ok(true)
2020-11-08 16:00:37 +01:00
}
/// Return `true` if two arrays are not-equal (i.e. any element not equal or not in the same order).
///
/// The operator `==` is used to compare elements and must be defined,
/// otherwise `false` is assumed.
///
/// # Example
///
/// ```rhai
/// let x = [1, 2, 3, 4, 5];
/// let y = [1, 2, 3, 4, 5];
/// let z = [1, 2, 3, 4];
///
/// print(x != y); // prints false
///
/// print(x != z); // prints true
/// ```
2021-03-09 06:16:05 +01:00
#[rhai_fn(name = "!=", return_raw, pure)]
2020-11-08 16:00:37 +01:00
pub fn not_equals(
ctx: NativeCallContext,
2021-08-13 07:42:39 +02:00
array1: &mut Array,
array2: Array,
2021-12-25 16:49:14 +01:00
) -> RhaiResultOf<bool> {
2021-08-13 07:42:39 +02:00
equals(ctx, array1, array2).map(|r| !r)
2020-11-08 16:00:37 +01:00
}
2020-10-18 07:18:12 +02:00
}