Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Array.prototype.filter method #262

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions boa/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,47 @@ pub fn slice(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
Ok(new_array)
}

/// Array.prototype.filter ( callback, [ thisArg ] )
///
/// For each element in the array the callback function is called, and a new
/// array is constructed for every value whose callback returned a truthy value
/// <https://tc39.es/ecma262/#sec-array.prototype.filter>
pub fn filter(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"missing argument 0 when calling function Array.prototype.filter",
));
}

let callback = args.get(0).cloned().unwrap_or_else(undefined);
let this_val = args.get(1).cloned().unwrap_or_else(undefined);

let length: i32 =
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");

let new = new_array(&interpreter)?;

let values = (0..length)
.filter_map(|idx| {
let element = this.get_field_slice(&idx.to_string());

let args = vec![element.clone(), to_value(idx), new.clone()];

let callback_result = interpreter
.call(&callback, &this_val, args)
.unwrap_or_else(|_| undefined());

if callback_result.is_true() {
Some(element)
} else {
None
}
})
.collect::<Vec<Value>>();

construct_array(&new, &values)
}

/// Create a new `Array` object
pub fn create_constructor(global: &Value) -> Value {
// Create Constructor
Expand All @@ -760,6 +801,7 @@ pub fn create_constructor(global: &Value) -> Value {
make_builtin_fn!(map, named "map", with length 1, of array_prototype);
make_builtin_fn!(fill, named "fill", with length 1, of array_prototype);
make_builtin_fn!(for_each, named "forEach", with length 1, of array_prototype);
make_builtin_fn!(filter, named "filter", with length 1, of array_prototype);
make_builtin_fn!(pop, named "pop", of array_prototype);
make_builtin_fn!(join, named "join", with length 1, of array_prototype);
make_builtin_fn!(to_string, named "toString", of array_prototype);
Expand Down
67 changes: 67 additions & 0 deletions boa/src/builtins/array/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -675,3 +675,70 @@ fn for_each_push_value() {
assert_eq!(forward(&mut engine, "a[6]"), "6");
assert_eq!(forward(&mut engine, "a[7]"), "8");
}

Nickforall marked this conversation as resolved.
Show resolved Hide resolved
#[test]
fn filter() {
let realm = Realm::create();
let mut engine = Executor::new(realm);

let js = r#"
var empty = [];
var one = ["1"];
var many = ["1", "0", "1"];

var empty_filtered = empty.filter(v => v === "1");
var one_filtered = one.filter(v => v === "1");
var zero_filtered = one.filter(v => v === "0");
var many_one_filtered = many.filter(v => v === "1");
var many_zero_filtered = many.filter(v => v === "0");
"#;

forward(&mut engine, js);

// assert the old arrays have not been modified
assert_eq!(forward(&mut engine, "one[0]"), String::from("1"));
assert_eq!(
forward(&mut engine, "many[2] + many[1] + many[0]"),
String::from("101")
);

// NB: These tests need to be rewritten once `Display` has been implemented for `Array`
// Empty
assert_eq!(
forward(&mut engine, "empty_filtered.length"),
String::from("0")
);

// One filtered on "1"
assert_eq!(
forward(&mut engine, "one_filtered.length"),
String::from("1")
);
assert_eq!(forward(&mut engine, "one_filtered[0]"), String::from("1"));

// One filtered on "0"
assert_eq!(
forward(&mut engine, "zero_filtered.length"),
String::from("0")
);

// Many filtered on "1"
assert_eq!(
forward(&mut engine, "many_one_filtered.length"),
String::from("2")
);
assert_eq!(
forward(&mut engine, "many_one_filtered[0] + many_one_filtered[1]"),
String::from("11")
);

// Many filtered on "0"
assert_eq!(
forward(&mut engine, "many_zero_filtered.length"),
String::from("1")
);
assert_eq!(
forward(&mut engine, "many_zero_filtered[0]"),
String::from("0")
);
}