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

3
我有一个深度嵌套对象的数组:
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 字段?

1
你需要针对顶层和嵌套层级分别进行递归,因为顶层的 name 属性会向下传递到其下面的所有层级以用于标签。 - Barmar
4个回答

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
你可以为嵌套层级使用不同的函数,这样你就可以将顶层的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));


1
你也可以使用 flatMap 递归地执行此操作,具体取决于是否已将 parent 传递到递归调用中:

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));


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)],或者在不需要存储列表的情况下迭代它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接