20

I have a function which sorts by name currently and an array of value / key pairs.

I wonder how can I pass the key on which sort is being performed so I can call the same function every time like so:

var arr = [{name:'bob', artist:'rudy'},
           {name:'johhny', artist:'drusko'},
           {name:'tiff', artist:'needell'},
           {name:'top', artist:'gear'}];

sort(arr, 'name');   //trying to sort by name
sort(arr, 'artist'); //trying to sort by artist

function sort(arr) {
  arr.sort(function(a, b) {
    var nameA=a.name.toLowerCase(), nameB=b.name.toLowerCase();
    if (nameA < nameB) //sort string ascending
      return -1;
    if (nameA > nameB)
      return 1;
    return 0; //default return value (no sorting)
   });          
}
thanksd
  • 54,176
  • 22
  • 157
  • 150
Toniq
  • 4,492
  • 12
  • 50
  • 109

5 Answers5

46
Array.prototype.sortOn = function(key){
    this.sort(function(a, b){
        if(a[key] < b[key]){
            return -1;
        }else if(a[key] > b[key]){
            return 1;
        }
        return 0;
    });
}



var arr = [{name:'bob', artist:'rudy'},{name:'johhny', artist:'drusko'},{name:'tiff', artist:'needell'},{name:'top', artist:'gear'}];

arr.sortOn("name");
arr.sortOn("artist");
Diode
  • 24,570
  • 8
  • 40
  • 51
20

[edit 2020/08/14] This was rather an old answer and not very good as well, so simplified and revised.

Create a function that returns the sorting lambda (the Array.prototype.sort callback that does the actual sorting). That function can receive the key name, the kind of sorting (string (case sensitive or not) or numeric) and the sorting order (ascending/descending). The lambda uses the parameter values (closure) to determine how to sort.

const log = (...strs) => 
  document.querySelector("pre").textContent += `\n${strs.join("\n")}`;
const showSortedValues = (arr, key) => 
  ` => ${arr.reduce((acc, val) => ([...acc, val[key]]), [])}`;
  
// the actual sort lamda factory function
const sortOnKey = (key, string, desc) => {
  const caseInsensitive = string && string === "CI";
  return (a, b) => {
    a = caseInsensitive ? a[key].toLowerCase() : a[key];
    b = caseInsensitive ? b[key].toLowerCase() : b[key];
    if (string) {
      return desc ? b.localeCompare(a) : a.localeCompare(b);
    }
    return desc ? b - a : a - b;
  }
};

// a few examples
const onNameStringAscendingCaseSensitive = 
  getTestArray().sort( sortOnKey("name", true) );
const onNameStringAscendingCaseInsensitive = 
  getTestArray().sort( sortOnKey("name", "CI", true) );
const onValueNumericDescending = 
  getTestArray().sort( sortOnKey("value", false, true) );

// examples
log(`*key = name, string ascending case sensitive`,
  showSortedValues(onNameStringAscendingCaseSensitive, "name")
);

log(`\n*key = name, string descending case insensitive`,
  showSortedValues(onNameStringAscendingCaseInsensitive, "name")
);

log(`\n*key = value, numeric desc`, 
  showSortedValues(onValueNumericDescending, "value")
);

function getTestArray() {
  return [{
    name: 'Bob',
    artist: 'Rudy',
    value: 23,
  }, {
    name: 'John',
    artist: 'Drusko',
    value: 123,
  }, {
    name: 'Tiff',
    artist: 'Needell',
    value: 1123,
  }, {
    name: 'Top',
    artist: 'Gear',
    value: 11123,
  }, {
    name: 'john',
    artist: 'Johanson',
    value: 12,
  }, ];
}
<pre></pre>
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • My mistake, your initial answer actually works with numeric sort (I just had strings instead of numbers). My next question is if key value is missing on some array items, how would I place those items on end of the search list? (so lets say you sort by 'artist' but second array item doesnt have 'artist' key value) Thanks – Toniq May 20 '13 at 14:34
  • See the edited answer. I would advise to prevent this kind of patching though and before sorting make sure the data to sort is consistent. – KooiInc May 20 '13 at 15:20
  • 2
    Its not working on the chrome browser.I dont know why its showing different behaviour than in other browsers .its works in firefox ,safari,IE.please give any suggestions regarding this thanks in advance – Sai Durga Sep 24 '14 at 04:28
  • xp values are numeric, not alphanumeric – Phaedrus Nov 11 '14 at 19:30
  • 1
    it has a strange behaviour as it doesn't work 100% of the times, depends probably on how many elements in the list, check here the snippet: https://stackoverflow.com/questions/46582119/sorting-list-based-on-a-property-using-js-sort-function – Giox Oct 05 '17 at 09:57
8
function keysrt(key) {
  return function(a,b){
   if (a[key] > b[key]) return 1;
   if (a[key] < b[key]) return -1;
   return 0;
  }
}

someArrayOfObjects.sort(keysrt('text'));
Phaedrus
  • 487
  • 7
  • 15
  • 2
    Looks like a duplicate of Diode's answer (but removed from the prototype) and should probably be removed. Also, beware that this algorithm will sort "Unicorn" before "astronaut" since capital letters have a smaller ASCII value than lowercase letters (at least I think that's why :) – Newclique Oct 04 '17 at 17:41
2

Make your life easy and use a closure https://stackoverflow.com/a/31846142/1001405

You can see the working example here

var filter = 'name', //sort by name
data = [{name:'bob', artist:'rudy'},{name:'johhny', artist:'drusko'},{name:'tiff', artist:'needell'},{name:'top', artist:'gear'}];; 

var compare = function (filter) {
    return function (a,b) { //closure
        var a = a[filter],
            b = b[filter];

        if (a < b) {
            return -1;
        }else if (a > b) {
            return 1;
        } else {
            return 0;
        }
    };
};

filter = compare(filter); //set filter

console.log(data.sort(filter));
Community
  • 1
  • 1
Michael Guild
  • 808
  • 9
  • 8
1

Looking at all the answers, I came up with my own solution that works cross-browser. The accepted solution does not work in IE or Safari. Also, the other solutions do not allow for sorting by descending.

/*! FUNCTION: ARRAY.KEYSORT(); **/
Array.prototype.keySort = function(key, desc){
  this.sort(function(a, b) {
    var result = desc ? (a[key] < b[key]) : (a[key] > b[key]);
    return result ? 1 : -1;
  });
  return this;
}

var arr = [{name:'bob', artist:'rudy'}, {name:'johhny', artist:'drusko'}, {name:'tiff', artist:'needell'}, {name:'top', artist:'gear'}];
arr.keySort('artist');
arr.keySort('artist', true);
Jake
  • 497
  • 1
  • 6
  • 16