0

I have the same problem with Converting flat structure to hierarchical and actually it solve from the url. But I want to have the result are sort by specific field (eg: sequence field).

let items = [
    {
        id: 1,
        parentId: null,
        name: 'A',
        sequence: 1
    },
    {
        id: 4,
        parentId: 1,
        name: 'A-2',
        sequence: 2
    },
    {
        id: 5,
        parentId: 1,
        name: 'A-1',
        sequence: 1
    },
    {
        id: 2,
        parentId: null,
        name: 'B',
        sequence: 2
    }
];

result:

[
    {
        id: 1,
        parentId: null,
        name: 'A',
        sequence: 1,
        children: [
            {
                id: 5,
                parentId: 1,
                name: 'A-1',
                sequence: 1
            },
            {
                id: 4,
                parentId: 1,
                name: 'A-2',
                sequence: 2
            },
        ]
    },
    {
        id: 2,
        parentId: null,
        name: 'B',
        sequence: 2
    }
];

Is there any custom code that should I use, so I can have the result also sorted by the sequence field ?

2 Answers2

0

/**
 * Sort an array of objects by property
 * @param {Array} arr Array of Objects
 * @param {String} p The property to sort
 * @param {boolean} desc Set to true for descend. sort
 */
const sortBy = (arr, p, desc) => arr.sort((a, b) => {
  if (desc) [a, b] = [b, a];
  const isNum = typeof b[p] === 'number';
  return (isNum ? Number(a[p]) - b[p] : String(a[p]).localeCompare(b[p]));
});

/**
 * Convert Linear Array to Tree Array
 * @param {Array} list to convert
 * @param {String} sort the property to sort
 */
const treefy = (list, sort) => {
  const map = list.reduce((m, li, i) => {
    m[li.id] = i;
    li.children = [];
    return m;
  }, {});
  return list.reduce((root, li) => {
    const arr = li.parentId ? list[map[li.parentId]].children : root;
    arr.push(li);
    if (sort) sortBy(arr, sort);
    return root;
  }, []);
}

// Use like:
const items = [
  {id:1, parentId:null, name:"B", sequence:2},
  {id:4, parentId:1, name:"A-2", sequence:2},
  {id:5, parentId:1, name:"A-1", sequence:1},
  {id:2, parentId:null, name:"A", sequence:1}
];
const tree = treefy(items, "sequence");
console.log(tree)
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
0

You could map out the ids and then iterate through the map in order to add references into the children of some of the elements:

let items = [{
    id: 1,
    parentId: null,
    name: 'A',
    sequence: 1
  },
  {
    id: 4,
    parentId: 1,
    name: 'A-2',
    sequence: 2
  },
  {
    id: 5,
    parentId: 1,
    name: 'A-1',
    sequence: 1
  },
  {
    id: 2,
    parentId: null,
    name: 'B',
    sequence: 2
  }
];

function tree(array) {
  let mapping = {};

  array.forEach(i => {
    mapping[i.id] = i; // Maps out the ids
  });

  array = [];

  for (let id in mapping) {
    let child = mapping[id];
    let {
      parentId
    } = child;
    if (parentId) { // If there is a parent
      let parent = mapping[parentId];
      if (!parent.children) parent.children = [child];
      else parent.children.push(child);
    }
  }

  for (let id in mapping) { // Add to the items ONLY the parents
    if (!mapping[id].parentId) array.push(mapping[id]);
  }

  return array.sort((a, b) => a.id - b.id); // Make sure that the ids are in sequential order
}

console.log(tree(items));
PotatoParser
  • 1,008
  • 7
  • 19