更新嵌套属性,没有多个循环

时间:2017-12-28 03:19:28

标签: javascript reactjs

我面临的一个问题是,在JavaScript中操纵数组或对象时,我总是重复自己。我们正逐渐远离jQuery年龄,我们可以简单地将统计数据转换为DOM或从[ { "data": [ { "name": "item1", "checked": false }, { "name": "item2", "checked": false } ] } ] 修改。

以此嵌套数组对象为例

checked

为了更改item2的{​​{1}}属性,我必须做2个循环

const newData = data.map(o=>{
  return {
    ...o,
    data: o.data.map(o2=>{
     if(o2.name === 'item2'){
       return{
         ...o2,
         'checked': true
       }
     }
     return o2
   })
  }
})

这个问题有更好的选择吗?

3 个答案:

答案 0 :(得分:1)

一种方法是认识到复杂数据结构中的每个嵌套级别都是一个"仿函数" (超出了这个答案的范围来解释它,但在线有很多很好的解释)。

仿函数-101的解释是它是" mappability"概念的概括,因此您可以映射数组,对象,可观察对象,各种怪异的东西,同时保留它们的结构。

无论如何,鉴于您的数据结构是嵌入在仿函数中的仿函数中的仿函数,您可以通过简单地将map函数与其自身组合在一起,可以映射到最深层次。嵌套仿函数结构。

E.g。在您的情况下,您可以使用map3来执行:

const result = map3(x => x.name === "item2" ? { ...x, checked: true } : x)(data)

这是一个完整的JS实现(我实现了FP库内联而不是使用依赖来阐明发生了什么):



// FP utils (you can use a library like Sanctuary or Ramda to get these)
const merge = (o1, o2) => ({
  ...o1,
  ...o2
})
const compose = fns => fns.reduce((f, g) => (...args) => f(g(...args)))

const mapArr = f => a => a.map(f)
const mapObj = f => o =>
  Object.keys(o)
    .map(k => ({
      [k]: f(o[k])
    }))
    .reduce(merge)
const mapImpls = new Map([[Object, mapObj], [Array, mapArr]])

const map = f => x => mapImpls.get(x.constructor)(f)(x)

const map2 = compose([map, map])
const map3 = compose([map, map, map])
const map4 = compose([map, map, map, map])
// ...

// Your code
const data = [
  {
    data: [
      {
        name: "item1",
        checked: false
      },
      {
        name: "item2",
        checked: false
      }
    ]
  }
]
const projection = x =>
  x.name === "item2"
    ? {
        ...x,
        checked: true
      }
    : x
const result = map3(projection)(data)
console.log(result)




据我所知,第一个给这个模式命名的人是Conal Elliott,在他的博客文章"语义编辑器组合器"。

答案 1 :(得分:0)

这取决于你如何构建你的州前。如果用这种方式表示相同的数据

{
 item1 : {name:"item1" ,checked:true} 
 ...
}

可能会导致无循环的简单操作,或者如果你有id,你也可以使用它们。
了解normalizr对构建状态link非常有帮助。

答案 2 :(得分:0)

let your_data = [
    {
    "data": [
        {
        "name": "item1",
        "checked": false
        },
        {
        "name": "item2",
        "checked": false
        }
    ]
    }
]
let data = [...your_data];
let index = 0, mod = 0, flag = false, rew_array = [];
while(index < data.length){
    if(!flag){
        new_array[index] = {...data[index]};
        mod = data[index].data.length;
        index = 0;
        flag = true;
    }else{
        let __index = new_array.length-1;
        let array = new_array[__index].data;
        if(array[index].name === 'item2'){
            array[index] = {
                ...array[index],
                'checked': true
            }
        }
        if(index > mod) {
            index = new_array.length;
            mod = 0;
            flag = false;
        }
    }
    index++;
}

console.log(data);

因为您已经知道数据的结构,所以可以通过单个while循环运行它,其中索引总是根据某个偏移量mod而变化。在这个例子中,我正在使用flag来告诉我何时检查外部数据或内部数据。并相应地改变指数。

尽管如此,我必须承认,你仍然无法解决这个事实,最终会做同样数量的迭代。