将对象数组转换为按值分组的数组数组

Convert array of objects to array of arrays grouped by values

在 fabric.js 应用程序中,我将元素分为几组。
用户可以形成包含任意数量元素的任意数量的组。
附图显示了分为三组的元素,每个元素都显示了它的 ID。

当用户完成组创建后,应用程序会测量元素之间的距离并将元素置于 "parent - child(ren)" 关系中。
这会生成一个对象数组:

var elements = [
    { "parent": 1, "children": [ 2 ] },
    { "parent": 2, "children": [ 1 ] },
    { "parent": 3, "children": [ 7 ] },
    { "parent": 4, "children": [ 5, 6 ] },
    { "parent": 5, "children": [ 4, 6, 7 ] },
    { "parent": 6, "children": [ 4, 5 ] },
    { "parent": 7, "children": [ 3, 5 ] },
    { "parent": 8, "children": [ 9 ] },
    { "parent": 9, "children": [ 8, 11 ] },
    { "parent": 10, "children": [ 11, 12 ] },
    { "parent": 11, "children": [ 9, 10 ] },
    { "parent": 12, "children": [ 10 ] }
];

组中的每个元素都是靠近它的元素的父元素,但同时它也是最靠近它的元素的子元素。
在此示例中,如何从 "elements" 数组中获取包含三个子数组的数组?
每个生成的子数组应包含仅在该组中的元素的 ID。
最后的结果应该是:

var groups = [
    [1, 2],
    [3, 4, 5, 6, 7],
    [8, 9, 10, 11, 12]
];

1) 通过遍历元素构建集合。对于每个元素,检查 parent 是否在任何现有集合中。如果没有可用的集合,则创建集合。将 children 添加到集合中。
2) 现在,我们有一组集合,可能有需要合并的相交集合。
3) 将集合数组转换为数组数组。

PS:我认为合并的第2步可以合并到第1步中。

var elements = [
  { parent: 1, children: [2] },
  { parent: 2, children: [1] },
  { parent: 3, children: [7] },
  { parent: 4, children: [5, 6] },
  { parent: 5, children: [4, 6, 7] },
  { parent: 6, children: [4, 5] },
  { parent: 7, children: [3, 5] },
  { parent: 8, children: [9] },
  { parent: 9, children: [8, 11] },
  { parent: 10, children: [11, 12] },
  { parent: 11, children: [9, 10] },
  { parent: 12, children: [10] }
];

const arrSets = [];

elements.forEach(({ parent, children }) => {
  let set = arrSets.find(set => set.has(parent));
  if (!set) {
    set = new Set();
    set.add(parent);
    arrSets.push(set);
  }
  children.forEach(x => set.add(x));
});

const resSets = [];
arrSets.forEach(set => {
  let curr = resSets.find(dat => [...set].some(x => dat.has(x)));
  if (!curr) {
    curr = new Set();
    resSets.push(curr);
  }
  [...set].forEach(x => curr.add(x));
});

const arr = resSets.map(set => [...set]);

console.log(arr);

这是使用堆栈解决问题的另一种方法。

  • 从父级开始,遍历它的子级和他们的子级直到堆栈为空。这是一组。
  • 维护一个已访问数组以避免循环
  • 如果组不为空,则将其推送到主数组

function findGrops(elements) {
  const eleMap = {};
  // build a parent => children map
  elements.forEach( e => {
    eleMap[e.parent] = e.children;
  });

  const groups = [];
  const visited = {};

  // iterate in a depth first way
  // parent -> children -> their children until empty
  Object.keys(eleMap).forEach( k => {
    let grp = [];
    let stk = [k]; //parseInt(k,10) to avoid the quotes
    while( stk.length > 0) {
      let x = stk.pop();
      if (!(x in visited)) {
        grp.push(x);
        visited[x] = true;
        // add children to the stack
        stk = stk.concat(eleMap[x]);
      }
    }
    // push to groups array
    grp.length && groups.push(grp);
  });

  return groups;
}

const input = [
    { "parent": 1, "children": [ 2 ] },
    { "parent": 2, "children": [ 1 ] },
    { "parent": 3, "children": [ 7 ] },
    { "parent": 4, "children": [ 5, 6 ] },
    { "parent": 5, "children": [ 4, 6, 7 ] },
    { "parent": 6, "children": [ 4, 5 ] },
    { "parent": 7, "children": [ 3, 5 ] },
    { "parent": 8, "children": [ 9 ] },
    { "parent": 9, "children": [ 8, 11 ] },
    { "parent": 10, "children": [ 11, 12 ] },
    { "parent": 11, "children": [ 9, 10 ] },
    { "parent": 12, "children": [ 10 ] }
];

  console.log('result:', findGrops(input));