根据对象属性对数据进行分组

3
我遇到了一个基于JavaScript对象属性实现分组的场景。
这是输入内容:
[
  {
    'A': 1,
    'B': 5,
    'C': 7,
    'D': 67
  },
  {
    'A': 1,
    'B': 5,
    'C': 7,
    'D': 69
  }
]

这是我期望的输出结果:
{
  name: 1,
  children: [
    {
      name: 5,
      children: [
        {
          name: 7,
          children: [
            {
              name: 67
            },
            {
              name: 69
            }
          ]
        }
      ]
    }
  ]
}

我寻找上述输出的原因是因为我计划在其中一个图表中使用它,该图表期望以这种方式获取数据。

另一个示例输入:

[
  {
    'A': 1,
    'B': 5,
    'C': 4,
    'D': 67
  },
  {
    'A': 1,
    'B': 5,
    'C': 7,
    'D': 69
  }
]

预期输出:
{
  name: 1,
  children: [
    {
      name: 5,
      children: [
        {
          name: 4,
          children: [
            {
              name: 67
            }
          ]
        },
        {
          name: 7,
          children: [
            {
              name: 69
            }
          ]
        }
      ]
    }
  ]
}

第一个属性A将始终保持不变,其余属性可以不同,并且基于这些属性的输出也可能会改变。

我试图传入输入数据和要根据其进行分组的键 - 在我的情况下是[A, B, C, D]。

  const cstmKey = keys[0];
  const grpdData = {
    name: data[0][cstmKey],
  };

  const grpdValues = data.reduce((acc, item) => {
    const customValue = item[cstmKey];
    if (!acc.includes(customValue)) {
      acc.push(customValue);
    }
    return acc;
  }, []);

我计划在 'grpdData' 上进行映射,但是没有想到任何实质性的东西。

为了排序,我试图根据另一个变量来实现,可以在其中定义顺序。例如:- const order = ['A', 'B', 'C', 'D']


字母的意义是什么?如果用"Foo"代替"C"会怎样?在确定深度时,键的顺序由什么决定 -- 插入顺序?词法顺序? - trincot
@trincot 我只是举个例子。我的意思是,在我的情况下,这些属性将保持不变。所以在这里,“C”将保持为“C”。 - Sam
嗯,如果只是一个例子的话,那你真的是在说它可能会有所不同,对吧?这里的变化因素是什么?是什么元素决定了群体的深度? - trincot
@trincot 对于订购,我尝试基于另一个变量来实现,以便我可以定义顺序。例如:- const order = ['A', 'B', 'C', 'D'] - Sam
好的。把那个信息加到问题中会很好。 - trincot
@trincot 我正在尝试使用一个不同的变量来确定深度以及排序(如上所述)。如果我在这里做错了什么,请告诉我。 - Sam
4个回答

1
通过分析情况,我认为你想要解构数组或对象,就像每个对象条目中都有另一个嵌套的对象一样。 试试这个方法,可能会对你有所帮助。

function createNestedObject(arr) {
    const result = {};
  
    for (const obj of arr) {
      let currentNode = result;
  
      for (const [key, value] of Object.entries(obj)) {
        if (!currentNode.children) {
          currentNode.children = [];
        }
  
        let nextNode = currentNode.children.find(node => node.name === value);
  
        if (!nextNode) {
          nextNode = { name: value };
          currentNode.children.push(nextNode);
        }
  
        currentNode = nextNode;
      }
    }
  
    return result.children[0];
  }
  
  const inputArray = [
    {
      'A': 1,
      'B': 5,
      'C': 7,
      'D': 67
    },
    {
      'A': 1,
      'B': 5,
      'C': 7,
      'D': 69
    }
  ];
  
  const nestedObject = createNestedObject(inputArray);
  console.log(JSON.stringify(nestedObject, null, 2));


根据目前的写法,你的回答不够清晰。请编辑以添加更多细节,帮助其他人理解这如何回答所提出的问题。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - Community

1

当您已经声明了输入和层次结构后,您可以使用以下算法来逐项创建您的树形结果。我想指出,您的最终结果也需要是一个数组(而不是对象),并且叶子项也具有一个名为children的字段,但它将为空。这符合树和其节点的一致性。

下面的代码解决了这个问题,我尝试解释每行代码的作用。

const input = [
    { 'A': 1, 'B': 5, 'C': 7, 'D': 67 },
    { 'A': 1, 'B': 5, 'C': 7, 'D': 69 }
];
const hierarchy = ['A', 'B', 'C', 'D'];

function getTree(input, hierarchy) {
    // declare tree
    const tree = [];
    // start cursor at tree root
    let cursor = tree;
    // loop through items of the input
    for (const branch of input) {
        // loop through each level of the hierarchy order
        for (const level of hierarchy) {
            // if node found, move on, if not, add it
            let node = cursor.find(({ name }) => (name === branch[level]));
            if (!node) {
                node = { name: branch[level], children: [] };
                cursor.push(node);
            }
            // move cursor forward
            cursor = node.children;
        }
        // hierarchy ended, reset cursor to tree root
        cursor = tree;
    }
    return tree;
}

console.log(getTree(input, hierarchy));


1
这应该可以工作。唯一的区别是名称将是字符串而不是数字。
这个方法通过两个步骤来实现:
1. 组成一个对象层次结构,其中键是“名称”。 2. 将该层次结构转换为所需的结构,包含名称/子元素。

function compute(inputs, keys){
  
  // first pass: build the hierarchy
  
  function next(node, input, idx){
    const key = keys[idx]
    const name = input[key]
    const child = node[name] = node[name] || {}
    if(idx < keys.length - 1){
      next(child, input, idx + 1)
    }
    return node
  }
  
    const result = inputs.reduce((acc, input) => next(acc, input, 0), {})
  
  // second pass: convert to the desired structure (name/children)
  
  function transform(node){
    return Object.keys(node).map(name => {
        const ret = {name}
      const children = transform(node[name])
      if(children.length){
        ret.children = children
      }
      return ret
    })
  }
  return transform(result)
}

const ids = ['A','B','C','D']
const inputs = [
  {
    'A': 1,
    'B': 5,
    'C': 7,
    'D': 67
  },
  {
    'A': 1,
    'B': 5,
    'C': 7,
    'D': 69
  }
]

const result = compute(inputs, ids)

document.write("<pre>" + JSON.stringify(result,null,4) + "</pre>")


1

我会首先创建一个嵌套对象,其中你输入对象的键也是嵌套对象中的键。所以对于你的例子,我会首先创建这样一个对象:

{
    "1": {
        "5": {
            "7": {
                "67": {},
                "69": {}
            }
        }
    }
}

然后使用递归函数将该对象转换为目标结构。
像这样:

function convert(data, keys) {
    const hierarchy = node => Object.entries(node).map(([name, child]) =>
            Object.keys(child).length ? { name, children: hierarchy(child) } : { name }
        );

    const root = {};
    for (const obj of data) {
        let node = root;
        for (const key of keys) node = (node[obj[key]] ??= {});
    }
    return hierarchy(root)[0];
}

// Example run
const data = [{'A': 1,'B': 5,'C': 7,'D': 67},{'A': 1,'B': 5,'C': 7,'D': 69}];
const result = convert(data, ["A", "B", "C", "D"]);
console.log(result);


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