Javascript 替换任意数据结构中的属性

Javascript 替换任意数据结构中的属性,javascript,recursion,ramda.js,Javascript,Recursion,Ramda.js,假设我有一个任意对象数组,我不知道它的结构。我希望以这样一种方式处理它:当在任何嵌套级别上发现与属性名称方面的某些条件相匹配的属性时,,就会对其执行一些操作(变异) 例如,“查找具有确切名称的所有属性”或“使用连字符查找所有属性”以及“将值替换为{redacted:true}” 我尝试了R.set和R.lensProp,但它似乎只作用于根级别的属性。假设我想用{redacted:true}替换baz值,或者当它是一个数组时在其上运行R.map const arr=[ { id:5, 姓名:"富

假设我有一个任意对象数组,我不知道它的结构。我希望以这样一种方式处理它:当在任何嵌套级别上发现与属性名称方面的某些条件相匹配的属性时,,就会对其执行一些操作(变异)

例如,“查找具有确切名称的所有属性”或“使用连字符查找所有属性”以及“将值替换为
{redacted:true}

我尝试了
R.set
R.lensProp
,但它似乎只作用于根级别的属性。假设我想用
{redacted:true}
替换
baz
值,或者当它是一个数组时在其上运行
R.map

const arr=[
{
id:5,
姓名:"富",,
巴兹:[
{
答:1,,
b:2
},
{
a:10,
b:5
}
],
其他:{
非常:{
嵌套的:{
巴兹:[
{
答:1,,
b:2
}
]
}
}
}
},
{
id:6,
名称:“酒吧”,
baz:[]
}
];
const result=R.set(
R.lensProp(“baz”),
{
是的,
},
arr[0]
);
控制台日志(结果)

可以采取的一种方法是递归地查找对象中与提供的键匹配的所有路径。下面的
findPaths
函数就是一个例子。这使得使用
R.chain
在单例数组中收集匹配路径,或者使用空数组忽略不需要的键。如果找到一个对象或数组,它将递归调用它们上面的
go
函数

有了要更新的所有路径的列表后,可以使用
R.assocPath
更新每个路径的值。例如,我使用
reduce
在下面的
redact
函数中迭代找到的每个路径

const indexedChain=R.addIndex(R.chain)
常量findPaths=(道具,对象)=>{
函数go(前缀,el){
if(R.is(Array,el))返回indexedChain((v,i)=>go(R.append(i,prefix),v),el)
if(R.is(Object,el))返回R.chain(k=>{
if(k==prop)返回[R.append(k,前缀)]
返回go(R.append(k,前缀),el[k])
},右键(el))
返回[]
} 
返回go([],obj)
}
常量编校=(道具,目标)=>
还原(
(obj_,path)=>R.assocPath(path,{redacted:true},obj_),
obj,
findPaths(道具,obj)
)
//////
常数arr=[
{
id:5,
姓名:"富",,
baz:[{a:1,b:2},{a:10,b:5}],
其他:{
非常:{
嵌套的:{
baz:[{a:1,b:2}]
}
}
}
},
{
id:6,
名称:“酒吧”,
baz:[]
}
]
//////
console.log(编辑('baz',arr))

您需要一个递归函数来处理:

  • 数组-映射和调用每个项上的自身
  • 其他对象-使用R.toPairs将对象转换为数组,映射对数组,在每个[key,value]对上调用谓词,并在也是数组的每个值上调用自身
  • 原语按原样返回
const{curry,cond,is,pipe,toPairs,map,when,last,evolve,identity,fromPairs,T}=R;
常量转换=curry((pred,arr)=>cond([
[是(数组),映射(a=>transform(pred,a))],//句柄数组-映射每个项
[is(Object),pipe(//处理非数组的对象
toPairs,//转换为对
映射(管道(//映射每对
pred,//对上的调用谓词
当(pipe(last,is(Object)),evolve([//处理数组值)
标识,//按原样返回密钥
a=>transform(pred,a)//变换数组
])),
)),
fromPairs//转换回数组
)],
[T,identity],//句柄原语
])(arr))
const redactBaz=变换(
R.when(R.pipe(R.head,R.equals('baz')),R.always(['reded',true]))
);
const arr=[{“id”:5,“name”:“foo”,“baz”:[{“a”:1,“b”:2},{“a”:10,“b”:5}],“other”:{“very”:{“nested”:{“baz”:[{“a”:1,“b”:2}]}}}},{“id”:6,“name”:“bar”,“baz”:;
const result=redactBaz(arr);
控制台日志(结果)

与Ori Drori的答案类似,我们可以在所有可能的JS类型上创建通用的自顶向下或自底向上遍历,通过将当前值以及相关的对象键或数组索引传入所提供的函数,允许在每一步进行转换

如果要在查找匹配节点时替换整个子树,则可以自顶向下处理此操作

const-mapindex=R.addIndex(R.map)
函数自底向上(f,val){
返回R.is(数组,val)?mapIndexed((v,i)=>f(自底向上(f,v,i),val)
:R.is(Object,val)?R.mapobjindex((v,i)=>f(bottomUp(f,v,i),val)
:f(val,null)
}
功能自上而下(f,val){
函数go(val,i){
常数res=f(val,i)
返回R.is(数组,res)?mapIndexed(go,res)
:R.is(对象,res)?R.mapobjindex(go,res)
:res
}
返回go(val,null)
}
/////
const arr=[{“id”:5,“name”:“foo”,“baz”:[{“a”:1,“b”:2},{“a”:10,“b”:5}],“other”:{“very”:{“nested”:{“baz”:[{“a”:1,“b”:2}]}}}},{“id”:6,“name”:“bar”,“baz”:;
常数结果=自上而下(
(v,k)=>k='baz'?{编辑:false}:v,
啊
)
console.log(结果)
通用厂房 我们可以编写一个相当通用的嵌套对象转换函数,它接受两个回调,一个是谓词,它给出一个键和一个值,决定我们是否要处理这个节点,另一个是谓词,它接受一个键和一个值,并返回一组键-值对。这使我们不仅可以过滤节点或变换单个节点,还可以创建新节点或将单个节点拆分为多个部分。不过,这具有强大的力量/巨大的责任感。编写动作回调可能比简单转换更难看。但是我们可以在上面编写更简单的函数来处理t
const addSequentialIds = transformSimple (
  (k, v) => Object (v) === v,
  ((n) => (obj) => ({_id: ++n, ...obj}))(0)
)

addSequentialIds ({foo: {bar: 'a', baz: {qux: 'b', corge: {grault: 'c'}}}})
//=> {foo: {_id :1, bar: 'a', baz: {_id: 2, qux: 'b', corge: {_id: 3, grault: 'c'}}}}