0

I'm trying to sort my array of objects in the following order:

  1. By ORDER First
  2. If a GROUP > 0 then matching group precedes over ORDER

Example:

var obj = [
        {order:1, title:"Receipts", group:0},
        {order:2, title:"Apples", group:1},
        {order:7, title:"Costs", group:0},
        {order:4, title:"Surplus", group:0},
        {order:5, title:"Bananas", group:1},
        {order:6, title:"Celery", group:2},
        {order:8, title:"Documents", group:0},
        {order:3, title:"Potatoes", group:2}
     ];

  
  var newObj = obj.sort(function(a,b) {
      return (a.order - b.order || b.group - a.group);
  });
  
  console.log(newObj);
//OUTPUT SHOULD LOOK LIKE THE FOLLOWING
/*

 var newObj = [
    {order:1, title:"Receipts", group:0},
    {order:2, title:"Apples", group:1},
    {order:5, title:"Bananas", group:1},        
    {order:3, title:"Potatoes", group:2}
    {order:6, title:"Celery", group:2},        
    {order:4, title:"Surplus", group:0},
    {order:7, title:"Costs", group:0},        
    {order:8, title:"Documents", group:0},        
 ];

  //ORDER OF LOGIC
   1. Function sorts by order. Sees that the first entry has GROUP = 0, continues.
   2. Function sees second entry has GROUP = 1. Function finds all objects with GROUP = 1
   3. After all GROUP=1 objects have been found, function continues by ORDER (next ORDER:3 is at the bottom). Function sees GROUP = 2.
   4. Function finds all objects with GROUP=2.
   5. After all GROUP=2 objects have been found, function continues by ORDER. The remaining objects have GROUP=0 so no changes made.

 */

I tried to do it the easy way by using map... but I'm can't figure out how to do this. Should I just loop through the objects and when I find a record, pull it out and re-loop back through the array to find similar objects?

Sanya
  • 1,270
  • 5
  • 21
  • 47
  • 1. Divide the elements up into groups. 2. Sort each group. 3. Merge each non-0 group into the 0-group based on the lowest-order element of that group. – Scott Hunter Sep 02 '19 at 22:58
  • 2
    Genuine curiosity: what is your real life use case for this? – pwilcox Sep 02 '19 at 23:07
  • @HereticMonkey That's the easy part. The hard part is dealing with group 0, whose elements should not be sorted together. – melpomene Sep 02 '19 at 23:24

1 Answers1

2

Array#sort is based on repeatedly comparing two elements from the array. If both elements have non-zero groups, it's easy: First compare the groups, then (if they have the same group), compare the order. Similarly, if both elements are in group 0, just compare the order.

Where it gets tricky is if one element has a group of 0 and the other a non-zero group. In that case you can't directly compare them: To figure out which element should come first in the result array, you have to look at the lowest order among all elements that are in the non-zero group. This information is not directly available.

We can make it available by making a pass through the array up front and storing the lowest order of each non-zero group in an associative array (minOf in the code below).

The comparison function first checks whether both elements have a non-zero group (or both have a group of 0). In either case we can do a normal two-field comparison (group first, then order).

Otherwise we need to compare the order field of the element in group 0 with the minOf value of the non-zero group.

The ... || -1 and ... || 1 fallbacks provide a consistent ordering in the case where two elements have the same order, but one has group 0 and the other doesn't.

function sortGrouped(arr) {
    let minOf = [];
    for (const x of arr) {
        if (x.group !== 0 && (minOf[x.group] === undefined || x.order < minOf[x.group])) {
            minOf[x.group] = x.order;
        }
    }
    return arr.sort((a, b) => {
        if ((a.group === 0) === (b.group === 0)) {
            return a.group - b.group || a.order - b.order;
        }
        return (
            a.group === 0
                ? a.order - minOf[b.group] || -1
                : minOf[a.group] - b.order || 1
        );
    });
}

const obj = [
    {order:1, title:"Receipts", group:0},
    {order:2, title:"Apples", group:1},
    {order:7, title:"Costs", group:0},
    {order:4, title:"Surplus", group:0},
    {order:5, title:"Bananas", group:1},
    {order:6, title:"Celery", group:2},
    {order:8, title:"Documents", group:0},
    {order:3, title:"Potatoes", group:2}
];

  
const newObj = sortGrouped(obj);
  
console.log(newObj);
melpomene
  • 84,125
  • 8
  • 85
  • 148