[英]Traversing recursively through an array and modifying values of object properties in JavaScript
这篇文章可能看起来很长,但很容易理解,如果没有,我会添加更多细节
我有看起来像的criteria
数组:
let criteria = [
"and",
{
"Collection": "persons",
"Property": "phone",
"operator": "eq",
"operatorValue": "$p:phone"
},
{
"Collection": "persondetails",
"Property": "country",
"operator": "eq",
"operatorValue": "$p:country"
},
["or",
{
"Collection": "persons",
"Property": "city",
"operator": "eq",
"operatorValue": "$p:city"
}]
]
criteria
特点:
{ "Collection": "persons", "Property": "phone", "operator": "eq", "operatorValue": "$p:phone" }
或者它可以是一个数组,如:
["or", { "Collection": "persons", "Property": "city", "operator": "eq", "operatorValue": "$p:city" }]
还有一个parameters
对象:
let parameters = {phone:"23138213", "country": "Russia", "city":"york"}
目的是递归遍历criteria
数组中的所有operatorValue
属性,如果遇到诸如$p:phone
,它将被替换为任何parameters["phone"]
评估为。
预期产出:
[
"and",
{
"Collection": "persons",
"Property": "phone",
"operator": "eq",
"operatorValue": "23138213"
},
{
"Collection": "persondetails",
"Property": "country",
"operator": "eq",
"operatorValue": "Russia"
},
["or",
{
"Collection": "persons",
"Property": "city",
"operator": "eq",
"operatorValue": "york"
}]
]
我能够递归遍历数组。 唯一的问题是我不知道如何修改原始criteria
变量。
请参阅 repl 中的第 43 行。 item[1]=parameters[item[1].split('$p:')[1]]
我理解为什么它不会修改标准,因为这里的 item 是完全不同范围内的不同变量。
失败的尝试:
function traverse(obj,parameters){
obj.forEach((item,index)=>{
if( typeof item == 'string' ){
//do nothing
}
else if( !(item instanceof Array)){
Object.entries(item).forEach((item,index)=>{
if( item[1] instanceof Array){
traverse(item,parameters);
}else{
if(item[1].startsWith('$p:')){
item[1]=parameters[item[1].split('$p:')[1]] //values dont get replaced for obvious reason
console.log(item[1])
}
}
})
}
else if( item instanceof Array){
traverse(item,parameters);
}
})
}
traverse(criteria,parameters)
console.log(criteria)
我该如何解决这个问题?
你可以简化你的功能。 您不需要遍历对象的条目。 您也不需要split
operationValue
。 parameters
映射键存在于Property
键中。
Array
。traverse
。parameters[val.Property]
更新它的operatorValue
属性 function traverse(arr, parameters) { for (const item of arr) { if (Array.isArray(item)) traverse(item, parameters) else if (typeof item === 'object') item.operatorValue = parameters[item.Property] } return arr } let criteria=["and",{Collection:"persons",Property:"phone",operator:"eq",operatorValue:"$p:phone"},{Collection:"persondetails",Property:"country",operator:"eq",operatorValue:"$p:country"},["or",{Collection:"persons",Property:"city",operator:"eq",operatorValue:"$p:city"}]], parameters = {phone:"23138213", "country": "Russia", "city":"york"}; console.log(traverse(criteria, parameters))
您需要获取对象的键来分配值。
function traverse(obj, parameters) { obj.forEach(item => { if (item instanceof Array) return traverse(item, parameters); if (item?.operatorValue?.startsWith('$p:')) { item.operatorValue = parameters[item.operatorValue.split('$p:')[1]]; } }); } const criteria = ["and", { Collection: "persons", Property: "phone", operator: "eq", operatorValue: "$p:phone" }, { Collection: "persondetails", Property: "country", operator: "eq", operatorValue: "$p:country" }, ["or", { Collection: "persons", Property: "city", operator: "eq", operatorValue: "$p:city" }]], parameters = { phone: "23138213", "country": "Russia", "city": "york" }; traverse(criteria, parameters); console.log(criteria);
.as-console-wrapper { max-height: 100% !important; top: 0; }
一个干净的递归版本:
const traverse = ([conj, ...nodes], params) => [ conj, ... nodes .map (node => Array .isArray (node) ? traverse (node, params) : {... node, operatorValue: params [nodes .Property]} ) ] const criteria = ["and", {Collection: "persons", Property: "phone", operator: "eq",operatorValue: "$p:phone"}, {Collection: "persondetails", Property: "country", operator: "eq", operatorValue: "$p:country"}, ["or", {Collection: "persons", Property: "city", operator: "eq", operatorValue: "$p:city"}]] const parameters = {phone:"23138213", "country": "Russia", "city":"york"} console .log (traverse (criteria, parameters))
.as-console-wrapper {min-height: 100% !important; top: 0}
这仅在 adiga 的简化证明对象的Property
节点与operatorValue
的后缀具有相同的值时才有效。 如果没有,我可能会使用正则表达式替换来处理它,并将其分成一个辅助函数:
const replaceOpValue = (params) => (obj) => ({
... obj,
operatorValue: obj .operatorValue .replace (/\$p:(.+)/, ((_, key) => params [key]))
})
const traverse = ([conj, ...nodes], params) => [
conj,
... nodes .map (node => Array .isArray (node)
? traverse (node, params)
: replaceOpValue (params) (node)
)
]
这是使用object-scan的解决方案。 根据您的要求,可能会更加灵活和可维护
// const objectScan = require('object-scan'); const criteria = [ 'and', { Collection: 'persons', Property: 'phone', operator: 'eq', operatorValue: '$p:phone' }, { Collection: 'persondetails', Property: 'country', operator: 'eq', operatorValue: '$p:country' }, [ 'or', { Collection: 'persons', Property: 'city', operator: 'eq', operatorValue: '$p:city' } ] ]; const substitute = (obj, params) => objectScan(['**.operatorValue'], { rtn: 'count', filterFn: ({ value, parent, property }) => { if (value.startsWith('$p:') && value.slice(3) in params) { parent[property] = params[value.slice(3)]; return true; } return false; } })(obj); console.log(substitute(criteria, { phone: '23138213', country: 'Russia', city: 'york' })); // returns number of substitutions // => 3 console.log(criteria); // => [ 'and', { Collection: 'persons', Property: 'phone', operator: 'eq', operatorValue: '23138213' }, { Collection: 'persondetails', Property: 'country', operator: 'eq', operatorValue: 'Russia' }, [ 'or', { Collection: 'persons', Property: 'city', operator: 'eq', operatorValue: 'york' } ] ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.8.0"></script>
免责声明:我是对象扫描的作者
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.