diff --git a/CHANGELOG.md b/CHANGELOG.md index 245cf35..6404a99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Decode logs returned by `Transaction.getReceipt()` when available in ABI * Add support for array concatenation +* Add `array.filter` function ### Other changes diff --git a/docs/src/builtin_methods.md b/docs/src/builtin_methods.md index f394387..d7010be 100644 --- a/docs/src/builtin_methods.md +++ b/docs/src/builtin_methods.md @@ -57,6 +57,16 @@ Applies the function `f` to each element of the array and returns a new array wi [2, 4, 6] ``` +### `array.filter(function p) -> array` + +Applies the predicate `p` to each element and only includes the elements for which the predicate returns `true`. + +```javascript +>> function isEven(x) { return x % 2 == 0; } +>> [1, 2, 3, 4, 5].filter(isEven) +[2, 4] +``` + ### `array.concat(array other) -> array` Concatenates two arrays. @@ -66,6 +76,7 @@ Concatenates two arrays. [1, 2, 3, 4] ``` + ## `tuple` methods ### `tuple[uint256 index] -> any` diff --git a/src/interpreter/builtins/iterable.rs b/src/interpreter/builtins/iterable.rs index 94a8128..13fb8d9 100644 --- a/src/interpreter/builtins/iterable.rs +++ b/src/interpreter/builtins/iterable.rs @@ -33,6 +33,31 @@ fn map<'a>( .boxed() } +fn filter<'a>( + env: &'a mut Env, + receiver: &'a Value, + args: &'a [Value], +) -> BoxFuture<'a, Result> { + async move { + let mut values = vec![]; + for v in receiver.get_items()? { + match args.first() { + Some(Value::Func(func)) => match func.execute(env, &[v.clone()]).await? { + Value::Bool(true) => values.push(v), + Value::Bool(false) => continue, + _ => bail!("filter function must return a boolean"), + }, + _ => bail!("filter function expects a function as an argument"), + }; + } + match receiver.get_type() { + Type::Array(t) => Ok(Value::Array(values, t.clone())), + ty => bail!("cannot map to type {}", ty), + } + } + .boxed() +} + pub fn iter_len(_env: &Env, arg: &Value) -> Result { arg.len().map(Into::into) } @@ -43,5 +68,10 @@ lazy_static! { map, vec![vec![FunctionParam::new("f", Type::Function)]] ); + pub static ref ITER_FILTER: Arc = AsyncMethod::arc( + "filter", + filter, + vec![vec![FunctionParam::new("p", Type::Function)]] + ); pub static ref ITER_LEN: Arc = SyncProperty::arc("length", iter_len); } diff --git a/src/interpreter/builtins/mod.rs b/src/interpreter/builtins/mod.rs index 5c87009..5e1b5e6 100644 --- a/src/interpreter/builtins/mod.rs +++ b/src/interpreter/builtins/mod.rs @@ -62,6 +62,7 @@ lazy_static! { let mut array_methods = HashMap::new(); array_methods.insert("length".to_string(), ITER_LEN.clone()); array_methods.insert("map".to_string(), iterable::ITER_MAP.clone()); + array_methods.insert("filter".to_string(), iterable::ITER_FILTER.clone()); array_methods.insert("concat".to_string(), concat::CONCAT.clone()); array_methods.insert("format".to_string(), format::NON_NUM_FORMAT.clone()); m.insert(NonParametricType::Array, array_methods);