Как создать объект дерева (parent-child) из массива в Javascript - PullRequest
1 голос
/ 07 ноября 2019

У меня есть массив вроде

[
"parent1|child1|subChild1",
"parent1|child1|subChild2",
"parent|child2|subChild1",
"parent1|child2|subChild2",
"parent2|child1|subChild1",
"parent2|child1|subChild2",
"parent2|child2|subChild1",
.
.
.    
]

, где моя первая строка перед |является родителем и второй строкой перед |это дочерний элемент и третья строка после второй |это подшилька

Как я могу преобразовать этот массив в объект как

[
 {
  "id": "parent1",
  "children":[
   {
    "id": "child1",
    "children":[
     {
      "id": "subChild1"
     }
    ]
   }
  ]
 }
]

Parent -> child -> subchild object

Основываясь на ответе Себастьяна, который я попробовал ниже, используя машинопись

private genTree(row) {
        let self = this;
        if (!row) {
            return;
        }
        const [parent, ...children] = row.split('|');
        if (!children || children.length === 0) {
            return [{
                id: parent,
                children: []
            }];
        }
        return [{
            id: parent,
            children: self.genTree(children.join('|'))
        }];
    }

    private mergeDeep(children) {
        let self = this;
        const res = children.reduce((result, curr) => {
            const entry = curr;
            const existing = result.find((e) => e.id === entry.id);
            if (existing) {
                existing.children = [].concat(existing.children, entry.children);
            } else {
                result.push(entry);
            }
            return result;
        }, []);
        for (let i = 0; i < res.length; i++) {
            const entry = res[i];
            if (entry.children && entry.children.length > 0) {
                entry.children = self.mergeDeep(entry.children);
            }
        };
        return res;
    }

private constructTree(statKeyNames){
    let self = this;
    const res = this.mergeDeep(statKeyNames.map(self.genTree).map(([e]) => e));
    console.log(res);
}

, но это дает мне сообщение «Невозможно прочитать свойство genTree of undefined» ошибка

Обновление:

В соответствии с комментарием Себастьяна изменил self.genTree на this.genTree.bind (это), и он работал без проблем

Ответы [ 2 ]

1 голос
/ 07 ноября 2019

Вы можете использовать объект mapper, который отображает каждый объект на его уникальный путь (Вы можете сопоставить объект с каждым id, но id здесь не уникален). Затем reduce каждый частичный элемент в массиве. Установите объект root как initialValue . Аккумулятор будет родительским объектом для текущего элемента. Возвращать текущий объект в каждой итерации.

const input = [
    "parent1|child1|subChild1",
    "parent1|child1|subChild2",
    "parent1|child2|subChild1",
    "parent1|child2|subChild2",
    "parent2|child1|subChild1",
    "parent2|child1|subChild2",
    "parent2|child2|subChild1"
  ],
  mapper = {},
  root = { children: [] }

for (const str of input) {
  let splits = str.split('|'),
      path = '';

  splits.reduce((parent, id, i) => {
    path += `${id}|`;

    if (!mapper[path]) {
      const o = { id };
      mapper[path] = o; // set the new object with unique path
      parent.children = parent.children || [];
      parent.children.push(o)
    }
    
    return mapper[path];
  }, root)
}

console.log(root.children)
1 голос
/ 07 ноября 2019

Вы должны использовать рекурсию для этого. Посмотрите здесь:

const arr = [
  "parent1|child1|subChild1",
  "parent1|child1|subChild2",
  "parent|child2|subChild1",
  "parent1|child2|subChild2",
  "parent2|child1|subChild1",
  "parent2|child1|subChild2",
  "parent2|child2|subChild1"
];

function genTree(row) {

  const [parent, ...children] = row.split('|');

  if (!children || children.length === 0) {
    return [{
      id: parent,
      children: []
    }];
  }

  return [{
    id: parent,
    children: genTree(children.join('|'))
  }];
};

function mergeDeep(children) {

  const res = children.reduce((result, curr) => {

    const entry = curr;

    const existing = result.find((e) => e.id === entry.id);
    if (existing) {

      existing.children = [].concat(existing.children, entry.children);
    } else {
      result.push(entry);
    }

    return result;
  }, []);

  for (let i = 0; i < res.length; i++) {

    const entry = res[i];
    if (entry.children && entry.children.length > 0) {
      entry.children = mergeDeep(entry.children);
    }
  };

  return res;
}

const res = mergeDeep(arr.map(genTree).map(([e]) => e));
console.log(JSON.stringify(res, false, 2));

Я использовал здесь двух помощников: genTree(row), который рекурсивно генерирует простое дерево из каждой строки, и mergeDeep(children), который уменьшает деревья первого уровня в результате arr.map(genTree).map(([e]) => e), а затем перебирает массив и рекурсивно делает то же самое со всеми children каждой записи .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...