我遇到一种情况,我需要使用动态过滤器(有时还需要复杂的过滤条件)来过滤数组。
我已经扩展了 Array 类来为此添加一个方法。只要我可以指定简单的过滤条件(例如 {gender: "m"}
),并且我当然可以和
其中的一些条件,这就可以正常工作。
不过,我还想做的是指定更复杂的过滤条件,基本上是 Array.prototype.filter()
方法的动态回调函数。我可以让它与 eval() 一起使用,但性能很糟糕,并且“不要使用 eval()”在互联网上随处可见。
您对如何解决这个问题有什么建议吗?任何关于安全有效地预编译复杂过滤字符串的建议,例如 ("key1==key2 || (last=="Smith"&& sex=="m")
<script>
var People = [
{ first: 'Peter', last: 'Henderson', key1:0, gender: "m", key2:0 },
{ first: 'Paul', last: 'Paulson', key1: 10, gender: "m", key2:10 },
{ first: 'Mary', last: 'Miller', key1: 2, gender: "f", key2:0 },
{ first: 'Mary', last: 'Smith', key1: 3 , gender: "f" , key2:3 },
{ first: 'Peter', last: 'Smith' , key1: 4, gender: "m", key2:0 }
];
console.log(People);
var newPeople = MyArray.from(People).filterBy("@key1 == @key2");
console.log(newPeople);
newPeople = MyArray.from(People).filterBy({gender: "m", last:"Smith"});
console.log(newPeople);
</script>
classs MyArray extends Array {
filterBy(argument) {
return this.filter(function(el){
if (typeof argument == "object") {
// object with key/parameter pairs for filtering:
// {key1: 0, gender: "m"}
for (let key in argument) {
if (argument[key] !== el[key]) return false;
};
return true;
} else if (typeof argument == "string") {
// string with logical expression with key names @-escaped, e.g.
// @gender == 'm' && @key1 == @key2
let expression = argument.split("@").join("el.")
return eval(expression);
}
});
}
}
最佳答案
考虑这个问题的一种方法是您希望实现一种过滤操作的语言。与任何语言一样,这种语言可以具体化为抽象语法树,以便可以动态传递和获取。
根据您问题中的单个示例("key1==key2 || (last=="Smith"&& sex=="m")
,假设您希望您的语言支持涉及文字值和输入字段的合取、析取和相等断言。
虽然不是绝对必要的,但将语言的 AST 建模为代数数据类型通常很方便。有很多库可以实现这一点,其中一个库的情况如下:
const { adt, match } = require("@masaeedu/adt")
// First, we build a language of references and predicates
const ref = adt({
lit: ["some literal value"],
key: ["the name of some field"]
})
const { lit, key } = ref
const predicate = adt({
and: ["predicate", "predicate"],
or: ["predicate", "predicate"],
equals: ["ref", "ref"]
})
const { and, or, equals } = predicate
// Then we construct an expression in this language
// NB: you could also use a parsing library to parse this out of a string
const yourpredicate =
or (equals (key ("key1")) (key ("key2")))
(and (equals (key ("last")) (lit ("Smith")))
(equals (key ("gender")) (lit ("m"))))
console.log(yourpredicate)
/* =>
{
label: "or",
values: [
{
label: "equals",
values: [
{ label: "key", values: ["key1"] },
{ label: "key", values: ["key2"] }
]
},
{
label: "and",
values: [
{
label: "equals",
values: [
{ label: "key", values: ["last"] },
{ label: "lit", values: ["Smith"] }
]
},
{
label: "equals",
values: [
{ label: "key", values: ["gender"] },
{ label: "lit", values: ["m"] }
]
}
]
}
]
}
*/
// Then we interpret our language into an actual predicate on some value
const resolve = ref.match({
lit: v => _ => v,
key: k => a => a[k]
})
const interpret = predicate.match({
and: f => g => a => interpret(f)(a) && interpret(g)(a),
or: f => g => a => interpret(f)(a) || interpret(g)(a),
equals: r1 => r2 => a => resolve(r1)(a) === resolve(r2)(a)
})
const inputs = [
{ key1: "foo", key2: "bar", last: "Smith", gender: "m" },
{ key1: "foo", key2: "foo", last: "Hurpenglurper", gender: "m" },
{ key1: "foo", key2: "bar", last: "Hurpenglurper", gender: "m" }
]
const p = interpret(yourpredicate)
console.log(inputs.map(p))
// =>
// [true, true, false]
您可以运行该示例并在此处进行操作:https://runkit.com/masaeedu/reified-predicates
关于javascript - 安全高效的动态多标准数组过滤javascript,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62344971/