简体   繁体   中英

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 just be sorted alphabetically along with other parent level items.

The desired output order would look something like this [ fizz, foobar, foobar xland, the shire, frogmorton, hobbiton]

Below is a code snippet of what I've tried so far, I'm fairly certain that chaining sorts together is a decent way to do this for code readability reasons but I'm having some issues with the logic behind the sorting rules.

 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 } ] var sortedArray = items .sort(function(first, second) { if(first.isParent && !second.isParent) { return -1 } else if(second.isParent && first._id != second.parent) { return 0 } else { return 1 } }) console.log("sortedArray", sortedArray) 

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: tp, data: t }; a.children = o[ai] && o[ai].children; o[ai] = a; if (ap === root) { r.push(a); } else { o[ap] = o[ap] || {}; o[ap].children = o[ap].children || []; o[ap].children.push(a); } }); return r; }(items.sort(function (a, b) { return a._id.localeCompare(b._id); }), undefined), ordered = tree.reduce(function flat(r, o) { return r.concat(o.data, (o.children || []).reduce(flat, [])); }, []); console.log(ordered); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

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:

 function depthFirst(items) { return Array.from(items.reduce( (mp, o) => (mp.get(o.parent).children.push(mp.get(o._id)), mp), new Map([{}].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(function collect(acc, o) { return acc.concat(o.orig, o.children.reduce(collect, [])) }, []); } // Sample input var 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)); 

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); 

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; } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM