我有一個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"
},
]
我想使用類似于的過濾條件物件(動態生成)過濾此陣列
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目的的過濾器陣列中的每個元件作為單個過濾器,每個option在options應滿足。可能的值的condition是is,is not,is less than和is greater than。
如何使用 JavaScript 以最有效的方式使用物件過濾options陣列conditions?
我嘗試過的(REPL 鏈接 - https://replit.com/@pcajanand/DarkseagreenEnlightenedTests#index.js),
做了一些過濾功能的創造者
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)
制作了一個函式來創建帶有單個過濾器的過濾器功能(型別 = 過濾器)
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))
}
}
Created filter functions and pushed them into an array,
let fnArray = []
if (filters.type === 'and') {
filters.filters.forEach((filter) => {
fnArray.push(makeFilterFunction(filter))
})
} else if (filters.type === 'filter') {
fnArray.push(makeFilterFunction(filters))
}
Loop through every option, check every filter condition against it, then pushed items passing all conditions to an array as filtered result.
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
}
}, [])
While this works (I think?), I want to know if there is some other more efficient way to do this. Or if I'm completely missing something and overcomplicating things.
TLDR - Want to filter an array of objects with multiple chained conditions.
Non-native English speaker here, sorry if the question is ambiguous. Thanks for reading!
uj5u.com熱心網友回復:
您實際上是在實作一種特定領域的語言,您需要將語言運算式轉換為可運行的程式。對于這種特定的語言,我們希望將運算式從純 JavaScript 物件轉換為 JavaScript函式-
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)}`)
}
}
然后我們將生成的函式直接插入到Array.prototype.filter. 基本用法如下所示 -
myinput.filter(evaluate({ /* your domain-specific expression here */ })
接下來evaluateFilter是您已經撰寫的低級函式。在這里它是作為一個單一的功能來實作的,但如果你愿意,你可以把它分開更多——
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
}
]
The evaluator is recursive so you can combine and and/or or in any imaginable way -
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
}
]
Expand the snippet to verify the result in your own browser -
Show code snippet
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"
}
]
}
]
})))
uj5u.com熱心網友回復:
您可以稍微簡化一下,這是一個示例:
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運行所有這些函式時是否會回傳。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/365193.html
標籤:javascript arrays reactjs json filtering
上一篇:并非所有資料都顯示在陣列中
