Сделать структуру вложенного объекта из плоской структуры и наоборот? - PullRequest
0 голосов
/ 07 июня 2018

У меня есть плоский массив.Например:

const inputArray = [
  {
    path: '1',
    id: '1'
  },
  {
    path: '2',
    id: '2'
  },
  {
    path: '3',
    id: '3'
  },
  {
    path: '3.4',
    id: '4'
  },
  {
    path: '3.5',
    id: '5'
  },
  {
    path: '3.4.6',
    id: '6'
  },
  {
    path: '3.4.7',
    id: '7'
  },
  {
    path: '8',
    id: '8'
  },
]

Где path - уникальный путь элемента по id.Например, path: '3.5' означает, что этот объект является дочерним для объекта с id: '3'path: '3.4.6' является дочерним для path: '3.4'.Я хочу собрать их во вложенную структуру.Таким образом, результат должен быть таким:

const result = [
  {
    path: '1',
    id: '1',
    children: []
  },
  {
    path: '2',
    id: '2',
    children: []
  },
  {
    path: '3',
    id: '3',
    children: [
      {
        path: '3.4',
        id: '4',
        children: [
           {
            path: '3.4.6',
            id: '6',
            children: []
          },
          {
            path: '3.4.7',
            id: '7',
            children: []
          },
        ]
      },
      {
        path: '3.5',
        id: '5',
        children: []
      },
    ]
  },
  {
    path: '8',
    id: '8',
    children: []
  },
]

А также мне нужен второй алгоритм, чтобы преобразовать их обратно, из вложенной в плоскую структуру.Можете ли вы дать рекламный совет, пожалуйста?

ОБНОВЛЕНИЕ: данные не отсортированы. Здесь - моя попытка, но кода слишком много, и в некоторых случаях происходит сбой.Я чувствую, что должен быть лучший способ сделать это.

Ответы [ 3 ]

0 голосов
/ 07 июня 2018

Использование Array.reduce , Array.findIndex , Array.push и Array.shift

Преобразовать в дерево

  • Предполагая, что input array отсортировано по пути, в противном случае вы не будете сортировать inputArray.sort((a,b) => a.path - b.path);
  • Уменьшитьмассив для формирования дерева
  • Создание массива иерархии путем разделения пути и создания из него массива чисел
  • Создание функции addChildren, которая будет принимать 3 входа
    • a -> Родительский объект (массив), в который будет вставлен объект
    • c -> объект, который необходимо вставить
    • t -> массив иерархии объекта, который необходимо вставить
  • Функция принимает первое значение t и, если оно является последним в иерархии, это означает, что a является допустимым заполнителем для объекта.Следовательно, толкни его туда.Если есть оставшиеся значения, найдите заполнитель из массива, сопоставив id.Теперь, снова вызов функции с a станет массивом children согласованного объекта , c останется прежним и t будет оставшимся массивом иерархии.

const inputArray = [{path:'1',id:'1'},{path:'2',id:'2'},{path:'3',id:'3'},{path:'3.4',id:'4'},{path:'3.5',id:'5'},{path:'3.4.6',id:'6'},{path:'3.4.7',id:'7'},{path:'8',id:'8'}];

const result = inputArray.reduce((a,c) => {
  let t = c.path.split(".").map(Number);
  addChildren(a,c,t);
  return a;
}, []);

function addChildren(a, c, t) {
  let val = t.shift();
  if(!t.length) {
    a.push({...c, children : []});
  } else {
    var i = a.findIndex(({id}) => Number(id) === val);
    addChildren(a[i].children, c, t);
  }
}
console.log(result);

Flatten Tree

  • Создание функции, которая принимает 2 входа
    • a -> массив ввода(Массив дочерних элементов)
    • r -> Результирующий массив
  • Функция выполняет итерацию по входному массиву, помещает объекты в массив результатов и проверяет наличие дочерних элементов, если да, а затем вызвать функцию для детей

var inputArray = [{path:'1',id:'1',children:[]},{path:'2',id:'2',children:[]},{path:'3',id:'3',children:[{path:'3.4',id:'4',children:[{path:'3.4.6',id:'6',children:[]},{path:'3.4.7',id:'7',children:[]},]},{path:'3.5',id:'5',children:[]},]},{path:'8',id:'8',children:[]},];

function flattenArray(a, r) {
  a.forEach(({children, ...rest}) => {
    r.push(rest);
    if(children) flattenArray(children, r)
  });
}
var result = [];
flattenArray(inputArray, result);
console.log(result);
0 голосов
/ 07 июня 2018

Если вы хотите больше гибкости, вы можете создать древовидную структуру из ввода, а затем делать с ней все, что захотите (например, выводить в каком-либо формате, который вы хотите, добавлять методы для поиска какого-либо элемента и т. Д.)

const inputArray = [
    {
        path: '1',
        id: '1'
    },
    {
        path: '2',
        id: '2'
    },
    {
        path: '3',
        id: '3'
    },
    {
        path: '3.4',
        id: '4'
    },
    {
        path: '3.5',
        id: '5'
    },
    {
        path: '3.4.6',
        id: '6'
    },
    {
        path: '3.4.7',
        id: '7'
    },
    {
        path: '8',
        id: '8'
    },
];

class Tree {
    constructor() {
        this.root = {};
    }

    addNewNode(path, node) {
        const pathArr = path.split('.');
        let currentNode = this.root;
        pathArr.forEach(item => {
            if (!currentNode[item]) {
                currentNode[item] = {};
            }
            currentNode = currentNode[item];
        });
        currentNode.data = node;
    }
}

const tree = new Tree();
inputArray.forEach(val => tree.addNewNode(val.path, val));
console.log(tree);

Вывод является выводом дерева, а не точным выводом, который вы включили - вам решать, как вы поступите с ним.

0 голосов
/ 07 июня 2018

Вы можете использовать объект в качестве вспомогательной структуры для несортированных данных и построить дерево.

Для получения плоского массива вы можете выполнить итерацию дерева и объединить плоские дочерние элементы с рекурсивной функцией.

function getTree(array) {
    var o = {};
    array.forEach(({ id, path }) => {
        var parents = path.split('.'),
            parent = parents[parents.length - 2];

        Object.assign(o[id] = o[id] || {}, { id, path });
        o[parent] = o[parent] || {};
        o[parent].children = o[parent].children || [];
        o[parent].children.push(o[id]);
    });
    return o.undefined.children;
}

function getFlat(array = []) {
    return array.reduce((r, { id, path, children }) =>
        r.concat({ id, path }, getFlat(children)), []);
}

var input = [{ path: '1', id: '1' }, { path: '2', id: '2' }, { path: '3', id: '3' }, { path: '3.4', id: '4' }, { path: '3.5', id: '5' }, { path: '3.4.6', id: '6' }, { path: '3.4.7', id: '7' }, { path: '8', id: '8' }],
    tree = getTree(input),
    flat = getFlat(tree);

console.log(tree);
console.log(flat);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Решение без сохранения path.

function getTree(array) {
    var o = {};
    array.forEach(({ id, path }) => {
        var parents = path.split('.'),
            parent = parents[parents.length - 2];

        Object.assign(o[id] = o[id] || {}, { id });
        o[parent] = o[parent] || {};
        o[parent].children = o[parent].children || [];
        o[parent].children.push(o[id]);
    });
    return o.undefined.children;
}

function getFlat(array = [], path = []) {
    return array.reduce((r, { id, children }) => {
        var p = path.concat(id);
        return r.concat({ id, path: p.join('.') }, getFlat(children, p));
    }, []);
}

var input = [{ path: '1', id: '1' }, { path: '2', id: '2' }, { path: '3', id: '3' }, { path: '3.4', id: '4' }, { path: '3.5', id: '5' }, { path: '3.4.6', id: '6' }, { path: '3.4.7', id: '7' }, { path: '8', id: '8' }],
    tree = getTree(input),
    flat = getFlat(tree);

console.log(tree);
console.log(flat);
.as-console-wrapper { max-height: 100% !important; top: 0; }
...