Javascript:将多维数组展平为唯一路径数组

时间:2018-02-07 11:12:00

标签: javascript arrays multidimensional-array tree

我试图建立一个树下所有独特路径的列表/ 多维对象数组。

假设这个数据......

const data = [
  {
    id: '1',
    items: [
      {
        id: '1.1',
        items: [ { id: '1.1.1' }, { id: '1.1.2' }, { id: '1.1.3' }, ]
      }
    ]
  },
  {
    id: '2',
    items: [
      {
        id: '2.1',
        items: [ { id: '2.1.1' }, { id: '2.1.2' }, { id: '2.1.3' },  ]
      },
      {
        id: '2.2',
        items: [ { id: '2.2.1' } ]
      }
    ]
  }
]

我需要最终得到像这样的数组结构..

const result = [
  ['1', '1.1', '1.1.1'],
  ['1', '1.1', '1.1.2'],
  ['1', '1.1', '1.1.3'],
  ['2', '2.1', '2.1.1'],
  ['2', '2.1', '2.1.2'],
  ['2', '2.1', '2.1.3'],
  ['2', '2.2', '2.2.1']
];

每个条目都是原始树结构中唯一path的数组。

我很难将每条路径作为单独的条目。到目前为止,我将它们返回到较低级别的路径,并将底层ID附加到当前路径。

function flatten(items, path = []) {
  let result = [];
  items.forEach( item => {
    path.push(item.id);
    if (item.items && item.items.length) {  
      result.push(flatten(item.items, path.slice(0) )); //slice to clone the array
    }
    else {
      result.push(...path);
    }
  });

  return result;
}

这是一个JS小提琴......

https://jsfiddle.net/9ptdm1ve/

3 个答案:

答案 0 :(得分:1)

您可以使用reduce()方法创建递归函数并返回数组。您可以使用concat()方法创建prev数组的副本,以便在每个递归级别上都有新副本,因为数组是通过引用传递的,否则您将更改原始数组。

const data = [{"id":"1","items":[{"id":"1.1","items":[{"id":"1.1.1"},{"id":"1.1.2"},{"id":"1.1.3"}]}]},{"id":"2","items":[{"id":"2.1","items":[{"id":"2.1.1"},{"id":"2.1.2"},{"id":"2.1.3"}]},{"id":"2.2","items":[{"id":"2.2.1"}]}]}]

function build(data, prev = []) {
  return data.reduce(function(r, e) {
    const copy = prev.concat(e.id)
    if (e.items) r = r.concat(build(e.items, copy))
    else r.push(copy)
    return r;
  }, [])
}

const result = build(data);
console.log(result)

答案 1 :(得分:0)

共享foreach回调中的更新路径。它应该是本地的。

function flatten(items, path = []) {
  let result = [];
  items.forEach(item => {
    let localPath = path.slice(0);
    localPath.push(item.id);
    if (item.items && item.items.length) {  
      result.push(flatten(item.items, localPath));
    } else {
      result.push(localPath);
    }
  });
  return result;
}

答案 2 :(得分:0)

您可以使用将所有完整路径推送到同一结果数组的内部方法:

const data = [{"id":"1","items":[{"id":"1.1","items":[{"id":"1.1.1"},{"id":"1.1.2"},{"id":"1.1.3"}]}]},{"id":"2","items":[{"id":"2.1","items":[{"id":"2.1.1"},{"id":"2.1.2"},{"id":"2.1.3"}]},{"id":"2.2","items":[{"id":"2.2.1"}]}]}]

function flatten(items) {
  const result = [];

  const iterateItems = (items, path = []) =>
    items.forEach(({ id, items }) => {
      const localPath = [...path, id];

      if (items)
        iterateItems(items, localPath);
      else
        result.push(localPath);
    });

  iterateItems(items);

  return result;
}

console.log(flatten(data));

另一种选择是使用Array.map(),并将每个阶段的结果按spreading展平为Array.concat()

const data = [{"id":"1","items":[{"id":"1.1","items":[{"id":"1.1.1"},{"id":"1.1.2"},{"id":"1.1.3"}]}]},{"id":"2","items":[{"id":"2.1","items":[{"id":"2.1.1"},{"id":"2.1.2"},{"id":"2.1.3"}]},{"id":"2.2","items":[{"id":"2.2.1"}]}]}];

const flatten = (arr, path = []) => 
  [].concat(...arr.map(({ id, items }) => items ? 
    flatten(items, [...path, id]) : [[...path, id]]
  ));
  

const result = flatten(data);

console.log(result);