4

I am trying to figure out an elegant way in ES6 to sort an array of objects based on specified values. Here's the scenario:

const list = [
{
"name": "john",
"lastName": "smith"
}, {
"name": "tony",
"lastName": "smith"
}, {
"name": "tony",
"lastName": "grey"
}, {
"name": "mary",
"lastName": "smith"
}, {
"name": "john",
"lastName": "x"
}, {
"name": "tom",
"lastName": "y"
}
, {
"name": "mary",
"lastName": "x"
}
]

let orderList = [{"name":["john","mary"]}, {"lastName":["x"]}];

So, basically sort the result by the names(John, Mary) and then sort that result by lastName(x), but the name sorting still takes precedence. The result should look like this:

[
  {
   "name": "john",
   "lastName": "x"
  }, {
   "name": "john",
   "lastName": "smith"
  }, {
    "name": "mary",
   "lastName": "x"
  }, {
   "name": "mary",
   "lastName": "smith"
  }, {
   "name": "tony",
   "lastName": "smith"
   }, {
    "name": "tony",
    "lastName": "grey"
   }, {
    "name": "tom",
    "lastName": "y"
   }
 ]

I have already tried doing something with group by, but it's a manual process for each name and last name.

_.groupBy(list , {"name": "john"});

I also tried experimenting with array reduce, but can't seem to find a good dynamic solution.

const sortArr = ['john', 'mary'];
const sortedList= list.reduce((result, element) => {
   let index = sortArr.findIndex(x => x === element.name);
   result[index !== -1
      ? index
      : result.length - 1].push(element); 
     return result;
       },[  [], [], [] ]);

Any help is appreciated. Thanks

scrape
  • 43
  • 3
  • Why not write a custom comparison function and use Array.sort – James Aug 24 '18 at 20:43
  • Possible duplicate of [How do you sort an array on multiple columns?](https://stackoverflow.com/questions/2784230/how-do-you-sort-an-array-on-multiple-columns) – Andreas Aug 24 '18 at 20:43
  • What are you trying to do exactly? Sort the people by first name and then put the one's with the last name 'x' first? – TheMagicalCake Aug 24 '18 at 20:44
  • The "order" array I guess is akin to a sql ORDER BY clause, so order by the first thing - name - with special rules, then by the second thing - lastName. – James Aug 24 '18 at 20:53

1 Answers1

3

You could use Array#sort and take an iterative approach for sorting first by name and then by lastName.

This proposal works with a check if a property is in an array and sorts these value to top by taking the delta of the indices. This is repeated until the delta is not zero or the array of order has ended.

For getting the delta, an Array#indexOf search is made and if -1, the value for not found item, is replaced with Infinity, because this item has to be sorted to the end of the array. The items with a found index are sorted according to the index.

For a bit faster sorting, objects with a single key/value pair are converted to an array with key and value.

var list = [{ name: "john", lastName: "smith" }, { name: "tony", lastName: "smith" }, { name: "tony", lastName: "grey" }, { name: "mary", lastName: "smith" }, { name: "john", lastName: "x" }, { name: "tom", lastName: "y" }, { name: "mary", lastName: "x" }],
    orderList = [{ name: ["john", "mary"] }, { lastName: ["x"] }],
    order = orderList.map(o => (k => [k, o[k]])(Object.keys(o)[0]));

list.sort((a, b) => {
    var d;
    order.some(([k, v]) =>
        d = (v.indexOf(a[k]) + 1 || Infinity) - (v.indexOf(b[k]) + 1 || Infinity)
    );
    return d;
});

console.log(list);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392