Skip to content

Commit

Permalink
Add filter, drain and retain to maps.
Browse files Browse the repository at this point in the history
  • Loading branch information
schungx committed Jun 13, 2024
1 parent f7728f7 commit 423f21e
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ New features

* The `break`, `continue`, `return` and `throw` statements can now follow the `??` operator to short-circuit operations where the value is `()`.
* A new symbol, `$func$`, is added to custom syntax to allow parsing of anonymous functions.
* The `filter`, `drain` and `retain` methods are added to object maps.


Version 1.18.0
Expand Down
160 changes: 159 additions & 1 deletion src/packages/map_basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

use crate::engine::OP_EQUALS;
use crate::plugin::*;
use crate::{def_package, Dynamic, ImmutableString, Map, NativeCallContext, RhaiResultOf, INT};
use crate::{
def_package, Dynamic, FnPtr, ImmutableString, Map, NativeCallContext, RhaiResultOf, INT,
};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;

Expand Down Expand Up @@ -293,6 +295,162 @@ mod map_functions {

map.values().cloned().collect()
}
/// Iterate through all the elements in the object map, applying a `filter` function to each
/// and return a new collection of all elements that return `true` as a new object map.
///
/// # Function Parameters
///
/// * `key`: current key
/// * `value` _(optional)_: copy of element (bound to `this` if omitted)
///
/// # Example
///
/// ```rhai
/// let x = #{a:1, b:2, c:3, d:4, e:5};
///
/// let y = x.filter(|k| this >= 3);
///
/// print(y); // prints #{"c":3, "d":4, "e":5}
///
/// let y = x.filter(|k, v| k != "d" && v < 5);
///
/// print(y); // prints #{"a":1, "b":2, "c":3}
/// ```
#[rhai_fn(return_raw)]
pub fn filter(ctx: NativeCallContext, map: &mut Map, filter: FnPtr) -> RhaiResultOf<Map> {
if map.is_empty() {
return Ok(Map::new());
}

let mut result = Map::new();

for (key, item) in map.iter_mut() {
if filter
.call_raw_with_extra_args("filter", &ctx, Some(item), [key.into()], [], Some(1))?
.as_bool()
.unwrap_or(false)
{
result.insert(key.clone(), item.clone());
}
}

Ok(result)
}
/// Remove all elements in the object map that return `true` when applied the `filter` function and
/// return them as a new object map.
///
/// # Function Parameters
///
/// * `key`: current key
/// * `value` _(optional)_: copy of element (bound to `this` if omitted)
///
/// # Example
///
/// ```rhai
/// let x = #{a:1, b:2, c:3, d:4, e:5};
///
/// let y = x.drain(|k| this < 3);
///
/// print(x); // prints #{"c":3, "d":4, "e":5]
///
/// print(y); // prints #{"a":1, "b"2}
///
/// let z = x.drain(|k, v| k == "c" || v >= 5);
///
/// print(x); // prints #{"d":4}
///
/// print(z); // prints #{"c":3, "e":5}
/// ```
#[rhai_fn(return_raw)]
pub fn drain(ctx: NativeCallContext, map: &mut Map, filter: FnPtr) -> RhaiResultOf<Map> {
if map.is_empty() {
return Ok(Map::new());
}

let mut drained = Map::new();
let mut retained = Map::new();

for (key, mut value) in mem::take(map).into_iter() {
if filter
.call_raw_with_extra_args(
"drain",
&ctx,
Some(&mut value),
[key.clone().into()],
[],
Some(1),
)?
.as_bool()
.unwrap_or(false)
{
drained.insert(key, value);
} else {
retained.insert(key, value);
}
}

*map = retained;

Ok(drained)
}
/// Remove all elements in the object map that do not return `true` when applied the `filter` function and
/// return them as a new object map.
///
/// # Function Parameters
///
/// * `key`: current key
/// * `value` _(optional)_: copy of element (bound to `this` if omitted)
///
/// # Example
///
/// ```rhai
/// let x = #{a:1, b:2, c:3, d:4, e:5};
///
/// let y = x.retain(|k| this < 3);
///
/// print(x); // prints #{"a":1, "b"2}
///
/// print(y); // prints #{"c":3, "d":4, "e":5]
///
/// let z = y.retain(|k, v| k == "c" || v >= 5);
///
/// print(y); // prints #{"c":3, "e":5}
///
/// print(z); // prints #{"d":4}
/// ```
#[rhai_fn(return_raw)]
pub fn retain(ctx: NativeCallContext, map: &mut Map, filter: FnPtr) -> RhaiResultOf<Map> {
if map.is_empty() {
return Ok(Map::new());
}

let mut drained = Map::new();
let mut retained = Map::new();

for (key, mut value) in mem::take(map).into_iter() {
if filter
.call_raw_with_extra_args(
"retain",
&ctx,
Some(&mut value),
[key.clone().into()],
[],
Some(1),
)?
.as_bool()
.unwrap_or(false)
{
retained.insert(key, value);
} else {
drained.insert(key, value);
}
}

*map = retained;

Ok(drained)
}

/// Return the JSON representation of the object map.
///
/// # Data types
Expand Down

0 comments on commit 423f21e

Please sign in to comment.