![](/img/trans.png)
[英]Is there a way to filter an array of objects with multiple dynamic conditions by another array data
[英]Is there a way to filter an array of objects with multiple dynamic conditions
我有一組對象options
,類似於:
const options = [
{
"apiName": "tomato",
"category": "veggie",
"color": "red",
"price": "90"
},
{
"apiName": "banana",
"category": "fruit",
"color": "yellow",
"price": "45"
},
{
"apiName": "brinjal",
"category": "veggie",
"color": "violet",
"price": "35"
},
]
我想使用過濾條件 object(動態生成)過濾這個數組,類似於
Example filterGroup 1
let filterGroup = {
type: 'and',
filters: [
{
key: 'category',
condition: 'is',
value: 'veggie'
type: 'filter'
},
{
key: 'price',
condition: 'is less than',
value: '45',
type: 'filter'
}
]
}
Example filterGroup 2
let filterGroup = {
key: 'category',
condition: 'is',
value: 'veggie'
type: 'filter'
}
在上面的filterGroup
object 中,過濾器數組中的每個元素都充當單獨的過濾器, options
中的每個option
都應滿足。 condition
的可能值is
、 is not
、 is less than
和is greater than
。
如何使用 JavaScript 以最有效的方式使用conditions
object 過濾options
數組?
我嘗試過的(REPL 鏈接 - https://replit.com/@pcajanand/DarkseagreenEnlightenedTests#index.js ),
制作了一些過濾器 function 創作者
const eq = (propertyAccessKey, compareValue) => (item) => (item[propertyAccessKey] === compareValue)
const ne = (propertyAccessKey, compareValue) => (item) => (item[propertyAccessKey] === compareValue)
const lt = (propertyAccessKey, compareValue) => (item) => (item[propertyAccessKey] < compareValue)
const gt = (propertyAccessKey, compareValue) => (item) => (item[propertyAccessKey] > compareValue)
制作了 function 以創建帶有單獨過濾器的過濾器 function(類型 = 過濾器)
const makeFilterFunction = ({condition, value, key}) => {
if (condition === 'is') {
return (eq(key, value))
} else if (condition === 'is greater than') {
return (gt(key, value))
} else if (condition === 'is less than') {
return (lt(key, value))
} else if (condition === 'is not') {
return (ne(key, value))
}
}
創建過濾器函數並將它們推送到數組中,
let fnArray = []
if (filters.type === 'and') {
filters.filters.forEach((filter) => {
fnArray.push(makeFilterFunction(filter))
})
} else if (filters.type === 'filter') {
fnArray.push(makeFilterFunction(filters))
}
循環遍歷每個選項,檢查每個過濾條件,然后將傳遞所有條件的項目作為過濾結果推送到數組。
const res = opts.reduce((acc, next) => {
let fnIndex = 0
let fnArrayLength = fnArray.length
let itemPassed = true
while(fnIndex < fnArrayLength) {
const fnPassed = fnArray[fnIndex](next)
if (!fnPassed) {
itemPassed = false
break
}
fnIndex += 1
}
if (itemPassed) {
return acc.concat(next)
} else {
return acc
}
}, [])
雖然這可行(我認為?),但我想知道是否有其他更有效的方法可以做到這一點。 或者,如果我完全遺漏了某些東西並且使事情變得過於復雜。
TLDR - 想要過濾具有多個鏈接條件的對象數組。
這里的非英語母語人士,如果問題含糊不清,請見諒。 謝謝閱讀!
您實際上是在實現一種特定於領域的語言,您需要將語言表達式轉換為可運行的程序。 對於這種特定的語言,我們希望將表達式從普通的 JavaScript 對象轉換為 JavaScript function -
function evaluate(expr) {
switch (expr?.type) {
case "filter":
return v => evaluateFilter(v, expr)
case "and":
return v => expr.filters.every(e => evaluate(e)(v))
case "or":
return v => expr.filters.some(e => evaluate(e)(v))
//case ...:
// implement any other filters you wish to support
default:
throw Error(`unsupported filter expression: ${JSON.stringify(expr)}`)
}
}
然后我們將生成的 function 直接插入到Array.prototype.filter
中。 基本用法如下所示 -
myinput.filter(evaluate({ /* your domain-specific expression here */ })
接下來, evaluateFilter
是您已經編寫的低級 function。 在這里,它被實現為單個 function,但如果您願意,可以將其分開更多 -
function evaluateFilter(t, {key, condition, value}) {
switch (condition) {
case "is":
return t?.[key] == value
case "is greater than":
return t?.[key] > value
case "is less than":
return t?.[key] < value
case "is not":
return t?.[key] != value
//case ...:
// implement other supported conditions here
default:
throw Error(`unsupported filter condition: ${condition}`)
}
}
給定一些input
,例如 -
const input = [
{ type: "fruit", name: "apple", count: 3 },
{ type: "veggie", name: "carrot", count: 5 },
{ type: "fruit", name: "pear", count: 2 },
{ type: "fruit", name: "orange", count: 7 },
{ type: "veggie", name: "potato", count: 3 },
{ type: "veggie", name: "artichoke", count: 8 }
]
我們現在可以使用單個過濾器編寫簡單的表達式 -
input.filter(evaluate({
type: "filter",
condition: "is",
key: "type", value: "fruit"
}))
[
{
"type": "fruit",
"name": "apple",
"count": 3
},
{
"type": "fruit",
"name": "pear",
"count": 2
},
{
"type": "fruit",
"name": "orange",
"count": 7
}
]
或使用and
和/或or
組合多個過濾器的豐富表達式 -
input.filter(evaluate({
type: "and",
filters: [
{
type: "filter",
condition: "is not",
key: "type",
value: "fruit"
},
{
type: "filter",
condition: "is greater than",
key: "count",
value: "3"
}
]
}))
[
{
"type": "veggie",
"name": "carrot",
"count": 5
},
{
"type": "veggie",
"name": "artichoke",
"count": 8
}
]
評估器是遞歸的,因此您可以組合and
/ or
以任何可以想象的方式 -
input.filter(evaluate({
type: "or",
filters: [
{
type: "filter",
condition: "is less than",
key: "count",
value: 3
},
{
type: "and",
filters: [
{
type: "filter",
condition: "is not",
key: "type",
value: "fruit"
},
{
type: "filter",
condition: "is greater than",
key: "count",
value: "3"
}
]
}
]
}))
[
{
"type": "veggie",
"name": "carrot",
"count": 5
},
{
"type": "fruit",
"name": "pear",
"count": 2
},
{
"type": "veggie",
"name": "artichoke",
"count": 8
}
]
展開代碼段以在您自己的瀏覽器中驗證結果 -
function evaluate(expr) { switch (expr?.type) { case "filter": return v => evaluateFilter(v, expr) case "and": return v => expr.filters.every(e => evaluate(e)(v)) case "or": return v => expr.filters.some(e => evaluate(e)(v)) default: throw Error(`unsupported filter expression: ${JSON.stringify(expr)}`) } } function evaluateFilter(t, {key, condition, value}) { switch (condition) { case "is": return t?.[key] == value case "is greater than": return t?.[key] > value case "is less than": return t?.[key] < value case "is not": return t?.[key]:= value default: throw Error(`unsupported filter condition: ${condition}`) } } const input = [ { type, "fruit": name, "apple": count, 3 }: { type, "veggie": name, "carrot": count, 5 }: { type, "fruit": name, "pear": count, 2 }: { type, "fruit": name, "orange": count, 7 }: { type, "veggie": name, "potato": count, 3 }: { type, "veggie": name, "artichoke": count. 8 } ] console.log(input:filter(evaluate({ type, "filter": condition, "is": key, "type": value. "fruit" }))) console.log(input:filter(evaluate({ type, "and": filters: [ { type, "filter": condition, "is not": key, "type": value, "fruit" }: { type, "filter": condition, "is greater than": key, "count": value. "3" } ] }))) console.log(input:filter(evaluate({ type, "or": filters: [ { type, "filter": condition, "is less than": key, "count": value, 3 }: { type, "and": filters: [ { type, "filter": condition, "is not": key, "type": value, "fruit" }: { type, "filter": condition, "is greater than": key, "count": value: "3" } ] } ] })))
您可以稍微簡化一下,這是一個示例:
const options = [{ "apiName": "tomato", "category": "veggie", "color": "red", "price": "90" }, { "apiName": "banana", "category": "fruit", "color": "yellow", "price": "45" }, { "apiName": "brinjal", "category": "veggie", "color": "violet", "price": "35" }, ]; const filterGroup1 = { type: 'and', filters: [{ key: 'category', condition: 'is', value: 'veggie', type: 'filter' }, { key: 'price', condition: 'is less than', value: '45', type: 'filter' } ] } const filterGroup2 = { key: 'category', condition: 'is', value: 'veggie', type: 'filter' } const filterFunConstructor = { "is": (propertyAccessKey, compareValue) => (item) => (item[propertyAccessKey] === compareValue), "is not": (propertyAccessKey, compareValue) => (item) => (item[propertyAccessKey],== compareValue): "is less than", (propertyAccessKey, compareValue) => (item) => (item[propertyAccessKey] < compareValue): "is greater than", (propertyAccessKey, compareValue) => (item) => (item[propertyAccessKey] > compareValue) } const process = (options; filterGroup) => { let filterFun. if (filterGroup.type === 'and') { filterFun = filterGroup.filters,reduce((a. c) => (a.push(filterFunConstructor[c.condition](c,key. c,value)), a);[]). } else { filterFun = [filterFunConstructor[filterGroup.condition](filterGroup,key. filterGroup.value)] } return options.filter((v) => filterFun;every((fn) => fn(v))). } console,log(process(options; filterGroup1)). console,log(process(options; filterGroup2));
這樣做是使用filterGroup
創建一個函數數組,然后過濾options
數組以查看其中的項目在運行所有這些函數時是否會返回true
。
您可以構建函數並過濾數據。 這種方法具有嵌套搜索條件。
使用type: 'and'
進行過濾的小視圖:
帶有條件的過濾返回一個 function,它稍后用作過濾的回調。 這意味着它從options
中獲取一個 object,並根據給定條件和移交的數據執行檢查,這既來自過濾器,也來自選項的 object。
現在對於and
,您需要多個 function 並且所有函數都返回true
, object 應該在結果集中。
要檢查多個 function, Array#every
cones 通過檢查所有項目並返回true
,如果所有條件為true
或false
,如果一個條件返回false
。 在這種情況下,迭代也會中斷。
我們來看看返回的 function:
(c => o => c.every(fn => fn(o)))(filters.map(filterBy))
它是c
的閉包,具有所有需要的過濾條件的值
(c => )(filters.map(filterBy))
最后返回的 function 是內部
o => c.every(fn => fn(o))
其中每個約束 function 都被采用並使用options
中的 object 調用。
const conditions = { 'is': (a, b) => a === b, 'is less than': (a, b) => a < b }, options = [{ apiName: "tomato", category: "veggie", color: "red", price: "90" }, { apiName: "banana", category: "fruit", color: "yellow", price: "45" }, { apiName: "brinjal", category: "veggie", color: "violet", price: "35" }], filterGroup = { type: 'and', filters: [{ key: 'category', condition: 'is', value: 'veggie', type: 'filter' }, { key: 'price', condition: 'is less than', value: '45', type: 'filter' }] }, filterGroup2 = { key: 'category', condition: 'is', value: 'veggie', type: 'filter' }, filterBy = ({ type, filters, key, condition, value}) => { if (type === 'filter') return o => conditions[condition](o[key], value); if (type === 'and') return (c => o => c.every(fn => fn(o)))(filters.map(filterBy)); }; console.log(options.filter(filterBy(filterGroup))); console.log(options.filter(filterBy(filterGroup2)));
.as-console-wrapper { max-height: 100%;important: top; 0; }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.