Faly
Faly

Reputation: 13346

Sort an array of objects by dynamically provided list of object properties in a order by then by style

How to write a generic sorting function in the style orderBy thenBy that sort an array by a list of properties provided as an array.

var items = [{ name: "AA" prop1 : 12, prop2: 13, prop3: 5, prop4: 22 },
             { name: "AA" prop1 : 12, prop2: 13, prop3: 6, prop4: 23 },
             { name: "AA" prop1 : 12, prop2: 14, prop3: 5, prop4: 23 },
             { name: "AA" prop1 : 11, prop2: 13, prop3: 5, prop4: 22 },
             { name: "AA" prop1 : 10, prop2: 13, prop3: 9, prop4: 21 }
            ];
// sort by prop1 then by prop3 then by prop4:
var sortedItems = sortByThenBy(items, ["prop1", "prop3", "prop4"]);

// sort by prop1 then by prop3:
var sortedItems = sortByThenBy(items, ["prop1", "prop3"]);

Upvotes: 4

Views: 3229

Answers (5)

Faly
Faly

Reputation: 13346

Thanks for all great answers. For information, I discovered that a recursive approach is also an alternative solution

function sortByThenBy(items, keys) {
    return items.sort(function(it1, it2){return compare(it1, it2, keys);});
}

function compare(it1, it2, keys, index) {
    index = index || 0;
    var currentKey = keys[index];
    return it1[currentKey] < it2[currentKey] ? 1 : (it1[currentKey] > it2[currentKey] ? -1 : compare(it1, it2, keys, index + 1));
} 

Upvotes: 1

Pranav C Balan
Pranav C Balan

Reputation: 115212

Do it using Array#sort and Array#reduce methods.

function sortByThenBy(arr, props) {
  // apply custom sort function on array
  return arr.sort(function(a, b) {
    // generate compare function return value by 
    // iterating over the properties array
    return props.reduce(function(bool, k) {
      // if previous compare result is `0` then compare
      // with the next property value and return result
      return bool || (a[k] - b[k]);
      // set initial value as 0
    }, 0);
  })
}

var items = [{
  name: "AA",
  prop1: 12,
  prop2: 13,
  prop3: 5,
  prop4: 22
}, {
  name: "AA",
  prop1: 12,
  prop2: 13,
  prop3: 6,
  prop4: 23
}, {
  name: "AA",
  prop1: 12,
  prop2: 14,
  prop3: 5,
  prop4: 23
}, {
  name: "AA",
  prop1: 11,
  prop2: 13,
  prop3: 5,
  prop4: 22
}, {
  name: "AA",
  prop1: 10,
  prop2: 13,
  prop3: 9,
  prop4: 21
}];

console.log(sortByThenBy(items, ["prop1", "prop3", "prop4"]));

console.log(sortByThenBy(items, ["prop1", "prop3"]));

console.log(sortByThenBy(items, ["prop2", "prop3"]));


function sortByThenBy(arr, props) {
  return arr.sort(function(a, b) {
    return props.reduce(function(bool, k) {
      return bool || (a[k] - b[k]);
    }, 0);
  })
}
.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}

Upvotes: 2

Redu
Redu

Reputation: 26161

I think you can do as follows;

function sortBy(a,p){
  return a.sort(function(a,b){
                  var sp = p.find(k => a[k] - b[k]); // find the property to compare;
                  return a[sp] - b[sp];
                });
}

var items = [{ name: "AA", prop1 : 12, prop2: 13, prop3: 5, prop4: 26 },
             { name: "AA", prop1 : 12, prop2: 13, prop3: 6, prop4: 23 },
             { name: "AA", prop1 : 12, prop2: 14, prop3: 5, prop4: 23 },
             { name: "AA", prop1 : 11, prop2: 13, prop3: 5, prop4: 22 },
             { name: "AA", prop1 : 10, prop2: 13, prop3: 9, prop4: 21 }
            ];
console.log(sortBy(items,["prop1", "prop3", "prop4"]));
console.log(sortBy(items,["prop1", "prop3"]));
console.log(sortBy(items,["prop4", "prop3"]));

Upvotes: 0

VadimB
VadimB

Reputation: 5711

try this solution.I'm creating sequence with data used in comparision like data1|0000NumberData2|data3. After that using native sort JS function to compare this sequence.

To avoid string comparision problem with numbers - zero padding used 000000345

So you can specify fields to use and fields sequence.

var items = [{ name: "AA", prop1 : 12, prop2: 13, prop3: 5, prop4: 22 },
             { name: "AA", prop1 : 2, prop2: 13, prop3: 6, prop4: 23 },
             { name: "AB", prop1 : 12, prop2: 14, prop3: 5, prop4: 23 },
             { name: "AA", prop1 : 11, prop2: 13, prop3: 5, prop4: 22 },
             { name: "AA", prop1 : 10, prop2: 13, prop3: 9, prop4: 21 }];

// sort by prop1 then by prop3 then by prop4:
var sortedItems = sortByThenBy(items, ["prop1", "prop3", "prop4"]);
console.log(sortedItems);

// sort by prop1 then by prop3:
var sortedItems = sortByThenBy(items, ["prop1", "prop3"]);
console.log(sortedItems);

function sortByThenBy(items, props) {
  return items.sort(function(a, b) {
    var a_path = $.map(props, function(item) {
      var ret = a[item];
      return isNumber(ret) ? pad(ret, 10) : ret;
    }).join('|');
    
    var b_path = $.map(props, function(item) {
      var ret = b[item];
      return isNumber(ret) ? pad(ret, 10) : ret;
    }).join('|');
    
    return a_path > b_path ? 1 : (a_path == b_path ? 0 : -1);
  });
}
 
function isNumber(n) {
   return !isNaN(parseFloat(n)) && isFinite(n);
}

function pad(n, width) {
  n = n + '';
  return n.length >= width ? n : new Array(width - n.length + 1).join('0') + n;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386550

You could iterate the keys and use Array#some for getting the order value.

This proposal works with a short circuit, if a delta is truthy (!== 0), then the iteration breaks and the delta is returned to the sort callback.

function sortByThenBy(array, keys) {
    return array.sort(function (a, b) {
        var r = 0;
        keys.some(function (k) {
            return r = a[k] - b[k];
        });
        return r;
    });
}

var items = [{ name: "AA", prop1: 12, prop2: 13, prop3: 5, prop4: 22 }, { name: "AA", prop1: 12, prop2: 13, prop3: 6, prop4: 23 }, { name: "AA", prop1: 12, prop2: 14, prop3: 5, prop4: 23 }, { name: "AA", prop1: 11, prop2: 13, prop3: 5, prop4: 22 }, { name: "AA", prop1: 10, prop2: 13, prop3: 9, prop4: 21 }];

console.log(sortByThenBy(items, ["prop1", "prop3", "prop4"]));
console.log(sortByThenBy(items, ["prop1", "prop3"]));
console.log(sortByThenBy(items, ["prop3", "prop1", "prop4"]));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Related Questions