String-path 到树 (JavaScript)

String-path to Tree (JavaScript)

我有一个字符串格式的路径数组,如下所示:

[
  { _id: 'women/clothes/tops', count: 10 },
  { _id: 'women/clothes/suits', count: 5 },
  { _id: 'women/accessories', count: 2 },
  { _id: 'men/clothes', count: 1 },
]

我想将它们分组成这样的树结构:

[
  {
    _id: 'women',
    count: 17,
    children: [
      {
        _id: 'clothes',
        count: 15,
        children: [
          { _id: 'tops', count: 10 },
          { _id: 'suits', count: 5 }
        ]
      },
      {
        _id: 'accessories',
        count: 2
      }
    ]
  },
  {
    _id: 'men',
    count: 1,
    children: [
      {
        _id: 'clothes',
        count: 1
      }
    ]
  }
]

我会想象一种调用 reduce 方法的递归函数。但是我想不通到底是怎么回事。

编辑:

我设法接近这个解决方案。但是我仍然得到一个空的 object 键,当没有 children:[=15 时,我无法设法没有 children 键=]


const getTree = (array) => {
  return array.reduce((a, b) => {
    const items = b._id.replace('\/', '').split('/')
    return construct(a, b.count, items)
  }, {})
}

const construct = (a, count, items) => {
  const key = items.shift()

  if(!a[key]) {
    a[key] = {
      _id: key,
      count: count,
      children: []
    }
    a[key].children = items.length > 0 ? construct(a[key].children, count, items) : null
  }
  else {
    a[key].count += count
    a[key].children = items.length > 0 ? construct(a[key].children, count, items) : null
  }
  return a
}

我先创建了一个 object 树,然后将其转换为具有 children 结构的 object 数组。

注意:我在中间结构中的每个 object 上使用了 _count 属性 以便稍后在键上循环时(创建最终结构时),我可以忽略_id_count 都很容易,并且只循环“真正的 children” 键,这些键不以 _.

开头

写这篇文章之前我没有看你现在的attempt/solution,所以我的看起来很不一样。

const origData = [
  { _id: 'women/clothes/tops', count: 10 },
  { _id: 'women/clothes/suits', count: 5 },
  { _id: 'women/accessories', count: 2 },
  { _id: 'men/clothes', count: 1 },
];


const newObj = {};
for (let obj of origData) {
  //console.log(obj)
  const tempArr = obj._id.split('/');
  let tempHead = newObj; // pointer
  for (let idx in tempArr) {
    let head = tempArr[idx];
    if (!tempHead.hasOwnProperty(head)) {
      tempHead[head] = {};
    }
    tempHead = tempHead[head];
    tempHead._id = head;
    const currCount = tempHead._count || 0;
    tempHead._count = currCount + obj.count;
  }
  tempHead._count = obj.count;
}

console.log(newObj);
const finalArr = [];
let tempArrHead = finalArr; // pointer
let tempObjHead = newObj; // pointer

function recursiveStuff(currObj, currArr, copyObj) {
  let hasChildren = false;
  const keys = Object.keys(currObj).filter(a => !a.startsWith("_"));
  for (let key of keys) {
      hasChildren = true;
      const obj = {
        _id: currObj[key]._id,
        count: currObj[key]._count || 0,
        children: [],
      };
      currArr.push(obj);
      recursiveStuff(currObj[key], obj.children, obj)
  }
  if (hasChildren == false) {
    // console.log(copyObj);
    // there might be a more elegant way, but this works:
    delete copyObj.children;
  }
}

recursiveStuff(tempObjHead, tempArrHead)
console.log(finalArr);
.as-console-wrapper{
  max-height: 100% !important;
}

中间结构:

{
  "women": {
    "_id": "women",
    "_count": 17,
    "clothes": {
      "_id": "clothes",
      "_count": 15,
      "tops": {
        "_id": "tops",
        "_count": 10
      },
      "suits": {
        "_id": "suits",
        "_count": 5
      }
    },
    "accessories": {
      "_id": "accessories",
      "_count": 2
    }
  },
  "men": {
    "_id": "men",
    "_count": 1,
    "clothes": {
      "_id": "clothes",
      "_count": 1
    }
  }
}

最终结构:

[
  {
    "_id": "women",
    "count": 17,
    "children": [
      {
        "_id": "clothes",
        "count": 15,
        "children": [
          {"_id": "tops", "count": 10},
          {"_id": "suits", "count": 5}
        ]
      },
      {"_id": "accessories", "count": 2}
    ]
  },
  {
    "_id": "men",
    "count": 1,
    "children": [
      {"_id": "clothes", "count": 1}
    ]
  }
]