Skip to content Skip to sidebar Skip to footer

Sorting Array To Make Parent Items Come Before Children

How can I order the list below to ensure children items are always preceded by their parent item? Some items in the list will be neither a child or parent item, and they should ju

Solution 1:

This solution works for any depth and use a tree for getting dependent items in right order.

var items = [{ _id: "Fizz" }, { _id: "Frogmorton", parent: "The Shire" }, { _id: "Hobbiton", parent: "The Shire" }, { _id: "Foobar", isParent: true }, { _id: "Foobar Xland", parent: "Foobar" }, { _id: "The Shire", isParent: true }],
    tree = function (data, root) {
        var r = [], o = {};
        data.forEach(function (t) {
            var a = { i: t._id, parent: t.p, data: t };
            a.children = o[a.i] && o[a.i].children;
            o[a.i] = a;
            if (a.p === root) {
                r.push(a);
            } else {
                o[a.p] = o[a.p] || {};
                o[a.p].children = o[a.p].children || [];
                o[a.p].children.push(a);
            }
        });
        return r;
    }(items.sort(function (a, b) { return a._id.localeCompare(b._id); }), undefined),
    ordered = tree.reduce(functionflat(r, o) {
        return r.concat(o.data, (o.children || []).reduce(flat, []));
    }, []);

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

Solution 2:

You could use this function, which can deal with grand-grand-grand-...-children, by converting it to a temporary tree and performing a depth-first iteration in it -- all in a functional way:

functiondepthFirst(items) {
    returnArray.from(items.reduce( (mp, o) =>
        (mp.get(o.parent).children.push(mp.get(o._id)), mp),
        newMap([{}].concat(items).map( (o) => [o._id, { children: [], orig: o }] ))
    ), ([_, o]) =>
        (o.children.sort((a, b) => a.orig._id.localeCompare(b.orig._id)), o)
    )[0].children.reduce(functioncollect(acc, o) {
        return acc.concat(o.orig, o.children.reduce(collect, []))
    }, []);
}
// Sample inputvar items = [{
    "_id": "Frogmorton",
    "parent": "The Shire"
}, {
    "_id": "Hobbiton",
    "parent": "The Shire"
}, {
    "_id": "Foobar",
}, {
    "_id": "Foobar Xland",
    "parent": "Foobar"
}, {
    "_id": "Fizz"
}, {
    "_id": "The Shire"
}, {
    "_id": "added grandchild",
    "parent": "Frogmorton"
}];

console.log(depthFirst(items));

Solution 3:

The following solutions works

var items = [
  
  {
    "_id": "Frogmorton",
    "parent": "The Shire"
  },
  {
    "_id": "Hobbiton",
    "parent": "The Shire"
  },
  {
    "_id": "Foobar",
    "isParent": true
  },
  {
    "_id": "Foobar Xland",
    "parent": "Foobar"
  },
  {
    "_id": "Fizz"
  },
  {
    "_id": "The Shire",
    "isParent": true
  }
]

const sortedArray = items
  .filter(item => item.isParent || !item.parent)
  .sort((a, b) => a._id.localeCompare(b._id))
  .map(parent => [parent].concat(
      items
        .filter(item => item.parent === parent._id)
        .sort((a, b) => a._id.localeCompare(b._id)))
    )
  .reduce((acc, item) => acc.concat(item), [])
  
console.log(sortedArray);

Solution 4:

You could use a closure with hashing to have a self-contained method.

const items = [ { _id: "Frogmorton", parent: "The Shire" }, { _id: "Hobbiton", parent: "The Shire" }, { _id: "Foobar", isParent: true }, { _id: "Foobar Xland", parent: "Foobar" },{ _id: "Fizz" }, { _id: "The Shire", isParent: true }],
result = Object.entries(items.slice().reduce((c =>(a, b) =>
    (c['val'] = b.parent || b._id, c['parent'] = b.parent, !a[c.val] ?
      a[c.val] = [b] :
      c.parent ? a[c.val].push(b) :
      a[c.val].unshift(b), a)
  )({}), ({}))).sort((a, b) =>
    a[0].localeCompare(b[0])).map(x =>
    x[1]).reduce((x, i) => x.concat(i), []);
    
console.log(result);
.as-console-wrapper { max-height: 100%!important; top: 0; }

Post a Comment for "Sorting Array To Make Parent Items Come Before Children"