将 Objects 的数组转换为 Objects 的分组数组

Convert array of Objects into a grouped array of Objects Typescript

我正在尝试像这样转换 objects 的数组:

[{grandParentField:'grandParent1', parentField:'parent1', childField: 'child1'},
 {grandParentField:'grandParent1', parentField:'parent1', childField: 'child2'},
 {grandParentField:'grandParent2', parentField:'parent1', childField: 'child3'},
 {grandParentField:'grandParent2', parentField:'parent2', childField: 'child4'}]

进入这个表格:

[
{
  text: 'grandparent1',
  items: [
    {
      text: 'parent1',
      items: [{ text: 'child1' }, { text: 'child2' }]
    }
  ]
},
{
  text: 'grandparent2',
  items: [
    {
      text: 'parent1',
      items: [{ text: 'child3' }]
    },
    {
      text: 'parent2',
      items: [{ text: 'child4' }]
    }
  ]
}

]

这个Thread和我想要的很像,但又不完全。

children 永远是唯一的,但是 parents 可以有多个 grandparents.

老实说,我已经尝试了很多东西,我什至不确定应该包括哪一个作为让我最接近的例子。

类似这样的东西,但能够接收 Objects 的数组,并抽出 {text: string, items:[{text: string, items:[{text:string]]} 结构:

var groupBy = function(xs, key) {
  return xs.reduce(function(rv, x) {
    (rv[x[key]] = rv[x[key]] || []).push(x);
    return rv;
  }, {});
};

console.log(groupBy(['one', 'two', 'three'], 'length'));

// => {3: ["one", "two"], 5: ["three"]}

在不对类型过于痴迷的情况下,我会说您希望输出具有以下形状:

interface Tree {
    text: string,
    items?: Tree[]
}

因此,让我们创建一个名为 group() 的函数,它获取您的数组和您要按照处理顺序处理的键列表。因此,对于您的示例,它将像这样使用:

const data = [
    { grandParentField: 'grandParent1', parentField: 'parent1', childField: 'child1' },
    { grandParentField: 'grandParent1', parentField: 'parent1', childField: 'child2' },
    { grandParentField: 'grandParent2', parentField: 'parent1', childField: 'child3' },
    { grandParentField: 'grandParent2', parentField: 'parent2', childField: 'child4' }
];
    
const groupedData = group(data, "grandParentField", "parentField", "childField");

下面是group()的实现:

function group(data: Array<Record<string, string>>, key: string, ...otherKeys: string[]): Tree[] {
    const objMap: Record<string, any[]> = {}
    for (const d of data) {
        if (!(d[key] in objMap)) {
            objMap[d[key]] = []
        }
        objMap[d[key]].push(d);
    }
    return Object.keys(objMap).map(k => otherKeys.length ?
        {
            text: k,
            items: group(objMap[k], otherKeys[0], ...otherKeys.slice(1))
        } : {
            text: k
        }
    );

}

首先,我们将 data 中的元素分组到名为 objMap 的数组字典中,其中每个元素 d 进入 objMap 的键 [=20] =](如果 key"grandParentField",则第一个元素进入名为 "grandParent1" 的键)。

完成此分组后,我们通过遍历 objMap 的键 return 一个新数组。如果我们没有 otherKeys,我们只是 return 一个 {text: string} 元素的数组,使用 objMap 的键作为 text 字段。如果我们 do 有其他键,那么我们需要递归调用 group() 存储在 objMap 中适当键的元素。

您可以验证这是否适用于您的示例:

console.log(JSON.stringify(groupedData, undefined, 2));
/* [
  {
    "text": "grandParent1",
    "items": [
      {
        "text": "parent1",
        "items": [
          {
            "text": "child1"
          },
          {
            "text": "child2"
          }
        ]
      }
    ]
  },
  {
    "text": "grandParent2",
    "items": [
      {
        "text": "parent1",
        "items": [
          {
            "text": "child3"
          }
        ]
      },
      {
        "text": "parent2",
        "items": [
          {
            "text": "child4"
          }
        ]
      }
    ]
  }
] */

Playground link to code

递归方法,应该适用于您将提供的每个 n 嵌套输入:

const input =[{grandParentField:"grandParent1",parentField:"parent1",childField:"child1"},{grandParentField:"grandParent1",parentField:"parent1",childField:"child2"},{grandParentField:"grandParent2",parentField:"parent1",childField:"child3"},{grandParentField:"grandParent2",parentField:"parent2",childField:"child4"}];
 
const nestedGroupBy = (nodes, order, orderIdx = 0) => {
  const key = order[orderIdx]
  let grouped = nodes.reduce((acc, e, i) => {
    let node = acc.find(x => x.text == e[key])
    if (!node) {
      node = { text: e[key], items: [] }
      acc.push(node)
    }
    node.items ? node.items.push(e) : node.items = [e]
    return acc
  }, [])
  if (order[orderIdx + 1])
    grouped = grouped.map(e => ({
      text: e.text,
      items: nestedGroupBy(e.items, order, orderIdx + 1)
    }))
  else
    grouped = grouped.map(e => ({ text: e.text }) )
  return grouped
}
 
const res = nestedGroupBy(input, Object.keys(input[0]))

console.log(res)
.as-console-wrapper { max-height: 100% !important; top: 0; }