如何将嵌套对象数组递归转换为平面对象数组?

时间:2019-08-01 01:26:59

标签: javascript arrays object ecmascript-6

我有以下一系列深层嵌套的对象:

const data = [
    {
      name: "foo",
      children:[
        {
          count: 1,
          name: "A"
        },
        { 
          count: 2,
          name: "B"
        }
      ]
    },
    {
      name: "bar",
      children: [
        {
          count: 3,
          name: "C",
          children: [
            {
              count: 4,
              name: "D"
            }
          ]
        }
      ]
    }
  ]

我要转换的方式如下:

const expectedStructure = [
    {
      count: 1,
      name: "A",
      label: "foo = A"
    },
    {
      count: 2,
      name: "B",
      label: "foo = B"
    },
    {
      count: 3,
      name: "C",
      label: "bar = C"
    },
    {
      count: 4,
      name: "D",
      label: "bar = D"
    }
  ]

我创建了将嵌套数组转换为平面对象数组的递归函数。

这是我的代码:

function getChildren(array, result=[]) {
    array.forEach(({children, ...rest}) => {
        result.push(rest);
        if(children) {
            getChildren(children, result);
        }
    });
    return result;
}

这是我得到的输出:

[ { name: 'foo' },
  { count: 1, name: 'A' },
  { count: 2, name: 'B' },
  { name: 'bar' },
  { count: 3, name: 'C' },
  { count: 4, name: 'D' } ]

问题是我需要向输出数组中的每个对象添加label字段,而且如果不对最终数组进行多次迭代以进行所需的转换,就无法找到解决方案。如何在不大大增加函数复杂性的情况下正确插入label字段?

4 个答案:

答案 0 :(得分:2)

检查每次迭代是否当前项目是“父”项目,如果是,则重新分配label

const data = [{name:"foo",children:[{count:1,name:"A"},{count:2,name:"B"}]},{name:"bar",children:[{count:3,name:"C",children:[{count:4,name:"D"}]}]}];

function getChildren(array, result = [], label = "") {
  array.forEach(({ children, name, count }) => {
    if (!label || name[1]) {
      label = `${name} = `;
    }
    if (count) {
      result.push({ count, name, label: label + name });
    }
    if (children) {
      getChildren(children, result, label);
    }
  });
  return result;
}

const res = getChildren(data);

console.log(res);

答案 1 :(得分:1)

您可以对嵌套级别使用其他函数,因此可以将顶级name属性向下传递到所有这些递归级别。

function getTopChildren(array, result = []) {
  array.forEach(({
    name,
    children
  }) => {
    if (children) {
      getChildren(children, name, result);
    }
  });
  return result;
}

function getChildren(array, name, result) {
  array.forEach(({
    children,
    ...rest
  }) => {
    rest.label = `${name} = ${rest.name}`;
    result.push(rest);
    if (children) {
      getChildren(children, name, result);
    }
  });
}

const data = [{
    name: "foo",
    children: [{
        count: 1,
        name: "A"
      },
      {
        count: 2,
        name: "B"
      }
    ]
  },
  {
    name: "bar",
    children: [{
      count: 3,
      name: "C",
      children: [{
        count: 4,
        name: "D"
      }]
    }]
  }
]

console.log(getTopChildren(data));

答案 2 :(得分:1)

您还可以根据是否已将parent传递到递归调用中,使用flatMap进行递归操作:

const data = [{
    name: "foo",
    children: [{
        count: 1,
        name: "A"
      },
      {
        count: 2,
        name: "B"
      }
    ]
  },
  {
    name: "bar",
    children: [{
      count: 3,
      name: "C",
      children: [{
        count: 4,
        name: "D"
      }]
    }]
  }
];

function flatten(arr, parent = null) {
  return parent
    ? arr.flatMap(({name, count, children}) => [
        {name, count, label: `${parent} = ${name}`}, 
        ...flatten(children || [], parent)
      ]) 
    : arr.flatMap(({name, children}) => flatten(children || [], name));
}

console.log(flatten(data));

答案 3 :(得分:1)

有时候,对代码进行推理并使用生成器清楚地编写代码要容易一些。您可以从递归调用中yield*

const data = [{name: "foo",children:[{count: 1,name: "A"},{ count: 2,name: "B"}]},{name: "bar",children: [{count: 3,name: "C",children: [{count: 4,name: "D"}]}]}]

function* flat(input, n){
    if (!input) return
    if (Array.isArray(input)) {
        for (let item of input)
            yield* flat(item, n)
    }
    let _name = n || input.name

    if ('count' in input) {
        yield { count:input.count, name:input.name, label:`${_name} = ${input.name}`} 
    }
    yield* flat(input.children, _name)
    
}

let g = [...flat(data)]
console.log(g)

该函数返回一个生成器,因此,如果需要列表,则需要将其扩展到列表[...flat(data)]中;如果不需要存储列表,则需要对其进行迭代。