简体   繁体   中英

Ordering array with hierarchy by score, possibly using lodash. (nodejs)

I am a bit stuck on this one: I have a array of objects ordered according to a hierarchy ( parent_id ), something as follow:

let myArray = [
 { id: 1, parent_id: null, score: 20, type: 1 },
 { id: 12, parent_id: 1, score: 25, type: 2 },
 { id: 23, parent_id: 12, score: 55, type: 3 },
 { id: 35, parent_id: 12, score: 25, type: 3 },
 { id: 10, parent_id: null, score: 75, type: 1 },
 { id: 25, parent_id: 10, score: 15, type: 2 },
 { id: 100, parent_id: 25, score: 88, type: 3 }
]

Now I would like to maintain the hierarchy order, but also order the elements by score to obtain something like this:

let expected = [
 { id: 10, parent_id: null, score: 75, type: 1 },
 { id: 25, parent_id: 10, score: 15, type: 2 },
 { id: 100, parent_id: 25, score: 88, type: 3 },
 { id: 1, parent_id: null, score: 20, type: 1 },
 { id: 12, parent_id: 1, score: 25, type: 2 },
 { id: 23, parent_id: 12, score: 55, type: 3 },
 { id: 35, parent_id: 12, score: 25, type: 3 },
]

I am writing pretty inefficient code with nested foreach that almost work, but nor quite yet. I was wondering if there was a neater solution. (pretty sure there is, but too smart for me). Also in my code I am relaying on the type attribute, but ideally I would not use it for sorting.

Note: this data is just an example, the real array is bigger and the number of children for each parent vary.

Since my explanation is not great we can think of hierarchy in this way type:1 -> Country type:2 -> State type:3 -> City

So I need to order by score desc like this

- Country
  - State
    - City 
    - City
  - State
    - City
 - Country and so on...

Thank you to anyone who is willing to land me an hand,

A single sort does not work, because of the parent children relation which is not respected while sorting the data.

This approach works in three parts:

  1. Sort data by score , because the following tree is build in insertation order.
  2. Build a tree with the given relationship.
  3. Traverse the tree and get sorted flat data back.

 var data = [{ id: 1, parent_id: null, score: 20, type: 1 }, { id: 12, parent_id: 1, score: 25, type: 2 }, { id: 23, parent_id: 12, score: 55, type: 3 }, { id: 35, parent_id: 12, score: 25, type: 3 }, { id: 10, parent_id: null, score: 75, type: 1 }, { id: 25, parent_id: 10, score: 15, type: 2 }, { id: 100, parent_id: 25, score: 88, type: 3 }] .sort(function (a, b) { return b.score - a.score; }), tree = function (data, root) { var r = [], o = {}; data.forEach(function (a) { o[a.id] = { data: a, children: o[a.id] && o[a.id].children }; if (a.parent_id === root) { r.push(o[a.id]); } else { o[a.parent_id] = o[a.parent_id] || {}; o[a.parent_id].children = o[a.parent_id].children || []; o[a.parent_id].children.push(o[a.id]); } }); return r; }(data, null), // null is the root value of parent_id sorted = tree.reduce(function traverse(r, a) { return r.concat(a.data, (a.children || []).reduce(traverse, [])); }, []) console.log(sorted); console.log(tree); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

I changed your data set to make the example easier to understand. And I used several functions to sort the data:

 let arr = [ {country: 'USA', state:'Washington', city:'Washington DC'}, {country: 'USA', state:'Washington', city:'Boston'}, {country: 'USA', state:'California', city:'San Diego'}, {country: 'Brazil', state:'North', city:'C'}, {country: 'Brazil', state:'North', city:'B'}, {country: 'Brazil', state:'South', city:'A'}, {country: 'Turkey', state:'East', city:'f'}, {country: 'Turkey', state:'East', city:'e'}, {country: 'Turkey', state:'West', city:'d'}, ]; let expected = [ {country: 'Brazil', state:'North', city:'B'}, {country: 'Brazil', state:'North', city:'C'}, {country: 'Brazil', state:'South', city:'A'}, {country: 'Turkey', state:'East', city:'e'}, {country: 'Turkey', state:'East', city:'f'}, {country: 'Turkey', state:'West', city:'d'}, {country: 'USA', state:'California', city:'San Diego'}, {country: 'USA', state:'Washington', city:'Boston'}, {country: 'USA', state:'Washington', city:'Washington DC'}, ]; const sortByCountry = arr => { return arr.sort((city1,city2) => city1.country > city2.country); }; const sortByState = arr => arr.sort( (city1,city2) => { // Don't compare when countries differ. if (city1.country !== city2.country) return 0; return city1.state > city2.state; } ); const sortByCity = arr => arr.sort( (city1,city2) => { // Don't compare when countries or states differ. if (city1.country !== city2.country) return 0; if (city1.state !== city2.state) return 0; return city1.city > city2.city; } ); let result = sortByCity(sortByState(sortByCountry(arr))); console.log('------expected-----'); console.log(expected); console.log('------result-----'); 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