4

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

4
  • where is sortByThenBy function declaration code? Commented Jan 23, 2017 at 14:33
  • @RomanPerekhrest this is what the user wants :) Commented Jan 23, 2017 at 14:33
  • not getting this, if u sort the same array by different key then the final result will be the same as just sort the array once by the final key, correct me if i m wrong ??? Commented Jan 23, 2017 at 14:34
  • @MayankShukla The goal is to sort the array by the first property and if some items have equal value for this properties, sort them by the second property and so on Commented Jan 23, 2017 at 14:40

5 Answers 5

2

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

Sign up to request clarification or add additional context in comments.

2 Comments

i think the result for 2nd case is not correct, please check.
sorry my mistake, didn't get the ques properly :)
1

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

Comments

1

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

Comments

0

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>

2 Comments

it does no work with different length of an item, like 10 vs 2.
For numbers - yes, because string comparision used. But you can use mask for digits like 00012 if you know range for your data, like here stackoverflow.com/questions/10073699/…
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.