How to sort an associative array by its values in Javascript?

Multi tool use
Multi tool use


How to sort an associative array by its values in Javascript?



I have the associative array:


array["sub2"] = 1;
array["sub0"] = -1;
array["sub1"] = 0;
array["sub3"] = 1;
array["sub4"] = 0;



What is the most elegant way to sort (descending) by its values where the result would be an array with the respective indices in this order:


sub2, sub3, sub1, sub4, sub0?





Since object properties have no language defined order - you can't (except, perhaps, in some JS engines by depending on the particular way that they implemented properties).
– Quentin
Mar 4 '11 at 22:23




10 Answers
10



Javascript doesn't have "associative arrays" the way you're thinking of them. Instead, you simply have the ability to set object properties using array-like syntax (as in your example), plus the ability to iterate over an object's properties.



The upshot of this is that there is no guarantee as to the order in which you iterate over the properties, so there is nothing like a sort for them. Instead, you'll need to convert your object properties into a "true" array (which does guarantee order). Here's a code snippet for converting an object into an array of two-tuples (two-element arrays), sorting it as you describe, then iterating over it:


var tuples = ;

for (var key in obj) tuples.push([key, obj[key]]);

tuples.sort(function(a, b) {
a = a[1];
b = b[1];

return a < b ? -1 : (a > b ? 1 : 0);
});

for (var i = 0; i < tuples.length; i++) {
var key = tuples[i][0];
var value = tuples[i][1];

// do something with key and value
}



You may find it more natural to wrap this in a function which takes a callback:




function bySortedValue(obj, callback, context) {
var tuples = ;

for (var key in obj) tuples.push([key, obj[key]]);

tuples.sort(function(a, b) {
return a[1] < b[1] ? 1 : a[1] > b[1] ? -1 : 0
});

var length = tuples.length;
while (length--) callback.call(context, tuples[length][0], tuples[length][1]);
}

bySortedValue({
foo: 1,
bar: 7,
baz: 3
}, function(key, value) {
document.getElementById('res').innerHTML += `${key}: ${value}<br>`
});


<p id='res'>Result:<br/><br/><p>





Thanks so much! This worked perfectly.
– John Smith
Mar 4 '11 at 22:38





The "obj" in "bySortedValue(obj" should be changed to "array" in order for the function to work properly.
– hakksor
Mar 5 '12 at 11:23





@hakksor — Goodness, but that went a while without getting caught! Thanks for letting me know.
– Ben Blank
Mar 6 '12 at 22:08






the tuples.sort function can be cleaned up to tuples.sort(function(a, b) { return a[1] - b[1]; });
– stot
Nov 30 '12 at 17:26






@stot — If your values are all numbers (as in the asker's example), absolutely. I appear to have absent-mindedly provided a comparison function which works with strings as well. :-)
– Ben Blank
Dec 6 '12 at 17:52



Instead of correcting you on the semantics of an 'associative array', I think this is what you want:


function getSortedKeys(obj) {
var keys = ; for(var key in obj) keys.push(key);
return keys.sort(function(a,b){return obj[b]-obj[a]});
}



You dump in an object (like yours) and get an array of the keys - eh properties - back, sorted descending by the (numerical) value of the, eh, values of the, eh, object.



This only works if your values are numerical. Tweek the little function(a,b) in there to change the sorting mechanism to work ascending, or work for string values (for example). Left as an exercise for the reader.


function(a,b)


string



EDIT: people keep upvoting this answer, but it's really old. Please
reconsider why you're not just using Object.keys() nowadays.





this should be an accepted answer.
– Dmitry Nazarov
Jan 20 '15 at 9:37





well, i keep getting downvotes on it :-)
– commonpike
Apr 25 '15 at 20:52





very elegant.. thank you!!
– geekinit
Aug 5 '15 at 15:44





This works perfectly and is less code then the above answer
– jshill103
Aug 17 '15 at 17:51





I should note that most browsers nowadays just support Object.keys() - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
– commonpike
Sep 2 '15 at 19:46




Continued discussion & other solutions covered at How to sort an (associative) array by value? with the best solution (for my case) being by saml (quoted below).



Arrays can only have numeric indexes. You'd need to rewrite this as either an Object, or an Array of Objects.


var status = new Array();
status.push({name: 'BOB', val: 10});
status.push({name: 'TOM', val: 3});
status.push({name: 'ROB', val: 22});
status.push({name: 'JON', val: 7});



If you like the status.push method, you can sort it with:


status.push


status.sort(function(a,b) {
return a.val - b.val;
});





Excellent! To do and alpha sort on name: return a.name > b.name.
– mpemburn
Mar 20 '14 at 19:38





@mpemburn: Not exactly, no
– Bergi
Jan 20 '15 at 23:25





For the alphabetic sort, compare the string values in the same case because sort() treats them differently. It messed me up for an hour until I found this stackoverflow.com/questions/6712034/… Example; a.name.toLowerCase() > b.name.toLowerCase()
– Dylan Valade
Oct 10 '16 at 20:39


sort()


a.name.toLowerCase() > b.name.toLowerCase()



There really isn't any such thing as an "associative array" in JavaScript. What you've got there is just a plain old object. They work kind-of like associative arrays, of course, and the keys are available but there's no semantics around the order of keys.



You could turn your object into an array of objects (key/value pairs) and sort that:


function sortObj(object, sortFunc) {
var rv = ;
for (var k in object) {
if (object.hasOwnProperty(k)) rv.push({key: k, value: object[k]});
}
rv.sort(function(o1, o2) {
return sortFunc(o1.key, o2.key);
});
return rv;
}



Then you'd call that with a comparator function.





+1 you beat me to it. I was writing a very similar explanation and piece of code.
– gonchuki
Mar 4 '11 at 22:32



Here is a variation of ben blank's answer, if you don't like tuples.



This saves you a few characters.


var keys = ;
for (var key in sortme) {
keys.push(key);
}

keys.sort(function(k0, k1) {
var a = sortme[k0];
var b = sortme[k1];
return a < b ? -1 : (a > b ? 1 : 0);
});

for (var i = 0; i < keys.length; ++i) {
var key = keys[i];
var value = sortme[key];
// Do something with key and value.
}



No unnecessary complication required...


function sortMapByValue(map)
{
var tupleArray = ;
for (var key in map) tupleArray.push([key, map[key]]);
tupleArray.sort(function (a, b) { return a[1] - b[1] });
return tupleArray;
}





The output of this is an array of arrays, not a map
– Daniel
Jul 2 '15 at 22:48





var stuff = {"a":1 , "b":3 , "c":0 } sortMapByValue(stuff) [Array[2], Array[2], Array[2]]
– Daniel
Jul 2 '15 at 22:49



var stuff = {"a":1 , "b":3 , "c":0 } sortMapByValue(stuff) [Array[2], Array[2], Array[2]]





I think this is cleanest. Just add a conversion to a map at the end
– Daniel
Jul 2 '15 at 22:59



i use $.each of jquery but you can make it with a for loop, an improvement is this:


//.ArraySort(array)
/* Sort an array
*/
ArraySort = function(array, sortFunc){
var tmp = ;
var aSorted=;
var oSorted={};

for (var k in array) {
if (array.hasOwnProperty(k))
tmp.push({key: k, value: array[k]});
}

tmp.sort(function(o1, o2) {
return sortFunc(o1.value, o2.value);
});

if(Object.prototype.toString.call(array) === '[object Array]'){
$.each(tmp, function(index, value){
aSorted.push(value.value);
});
return aSorted;
}

if(Object.prototype.toString.call(array) === '[object Object]'){
$.each(tmp, function(index, value){
oSorted[value.key]=value.value;
});
return oSorted;
}
};



So now you can do


console.log("ArraySort");
var arr1 = [4,3,6,1,2,8,5,9,9];
var arr2 = {'a':4, 'b':3, 'c':6, 'd':1, 'e':2, 'f':8, 'g':5, 'h':9};
var arr3 = {a: 'green', b: 'brown', c: 'blue', d: 'red'};
var result1 = ArraySort(arr1, function(a,b){return a-b});
var result2 = ArraySort(arr2, function(a,b){return a-b});
var result3 = ArraySort(arr3, function(a,b){return a>b});
console.log(result1);
console.log(result2);
console.log(result3);



Just so it's out there and someone is looking for tuple based sorts.
This will compare the first element of the object in array, than the second element and so on. i.e in the example below, it will compare first by "a", then by "b" and so on.


let arr = [
{a:1, b:2, c:3},
{a:3, b:5, c:1},
{a:2, b:3, c:9},
{a:2, b:5, c:9},
{a:2, b:3, c:10}
]

function getSortedScore(obj) {
var keys = ;
for(var key in obj[0]) keys.push(key);
return obj.sort(function(a,b){
for (var i in keys) {
let k = keys[i];
if (a[k]-b[k] > 0) return -1;
else if (a[k]-b[k] < 0) return 1;
else continue;
};
});
}

console.log(getSortedScore(arr))



OUPUTS


[ { a: 3, b: 5, c: 1 },
{ a: 2, b: 5, c: 9 },
{ a: 2, b: 3, c: 10 },
{ a: 2, b: 3, c: 9 },
{ a: 1, b: 2, c: 3 } ]



The best approach for the specific case here, in my opinion, is the one commonpike suggested. A little improvement I'd suggest that works in modern browsers is:


// aao is the "associative array" you need to "sort"
Object.keys(aao).sort(function(a,b){return aao[b]-aao[a]});



This could apply easily and work great in the specific case here so you can do:


let aoo={};
aao["sub2"]=1;
aao["sub0"]=-1;
aao["sub1"]=0;
aao["sub3"]=1;
aao["sub4"]=0;

let sk=Object.keys(aao).sort(function(a,b){return aao[b]-aao[a]});

// now you can loop using the sorted keys in `sk` to do stuffs
for (let i=sk.length-1;i>=0;--i){
// do something with sk[i] or aoo[sk[i]]
}



Besides of this, I provide here a more "generic" function you can use to sort even in wider range of situations and that mixes the improvement I just suggested with the approaches of the answers by Ben Blank (sorting also string values) and PopeJohnPaulII (sorting by specific object field/property) and lets you decide if you want an ascendant or descendant order, here it is:


// aao := is the "associative array" you need to "sort"
// comp := is the "field" you want to compare or "" if you have no "fields" and simply need to compare values
// intVal := must be false if you need comparing non-integer values
// desc := set to true will sort keys in descendant order (default sort order is ascendant)
function sortedKeys(aao,comp="",intVal=false,desc=false){
let keys=Object.keys(aao);
if (comp!="") {
if (intVal) {
if (desc) return keys.sort(function(a,b){return aao[b][comp]-aao[a][comp]});
else return keys.sort(function(a,b){return aao[a][comp]-aao[a][comp]});
} else {
if (desc) return keys.sort(function(a,b){return aao[b][comp]<aao[a][comp]?1:aao[b][comp]>aao[a][comp]?-1:0});
else return keys.sort(function(a,b){return aao[a][comp]<aao[b][comp]?1:aao[a][comp]>aao[b][comp]?-1:0});
}
} else {
if (intVal) {
if (desc) return keys.sort(function(a,b){return aao[b]-aao[a]});
else return keys.sort(function(a,b){return aao[a]-aao[b]});
} else {
if (desc) return keys.sort(function(a,b){return aao[b]<aao[a]?1:aao[b]>aao[a]?-1:0});
else return keys.sort(function(a,b){return aao[a]<aao[b]?1:aao[a]>aao[b]?-1:0});
}
}
}



You can test the functionalities trying something like the following code:


let items={};
items['Edward']=21;
items['Sharpe']=37;
items['And']=45;
items['The']=-12;
items['Magnetic']=13;
items['Zeros']=37;
//equivalent to:
//let items={"Edward": 21, "Sharpe": 37, "And": 45, "The": -12, ...};

console.log("1: "+sortedKeys(items));
console.log("2: "+sortedKeys(items,"",false,true));
console.log("3: "+sortedKeys(items,"",true,false));
console.log("4: "+sortedKeys(items,"",true,true));
/* OUTPUT
1: And,Sharpe,Zeros,Edward,Magnetic,The
2: The,Magnetic,Edward,Sharpe,Zeros,And
3: The,Magnetic,Edward,Sharpe,Zeros,And
4: And,Sharpe,Zeros,Edward,Magnetic,The
*/

items={};
items['k1']={name:'Edward',value:21};
items['k2']={name:'Sharpe',value:37};
items['k3']={name:'And',value:45};
items['k4']={name:'The',value:-12};
items['k5']={name:'Magnetic',value:13};
items['k6']={name:'Zeros',value:37};

console.log("1: "+sortedKeys(items,"name"));
console.log("2: "+sortedKeys(items,"name",false,true));
/* OUTPUT
1: k6,k4,k2,k5,k1,k3
2: k3,k1,k5,k2,k4,k6
*/



As I already said, you can loop over sorted keys if you need doing stuffs


let sk=sortedKeys(aoo);
// now you can loop using the sorted keys in `sk` to do stuffs
for (let i=sk.length-1;i>=0;--i){
// do something with sk[i] or aoo[sk[i]]
}



Last, but not least, some useful references to Object.keys and Array.sort



@commonpike's answer is "the right one", but as he goes on to comment...



most browsers nowadays just support Object.keys()


Object.keys()



Yeah.. Object.keys() is WAY better.


Object.keys()



But what's even better? Duh, it's it in coffeescript!


coffeescript


sortedKeys = (x) -> Object.keys(x).sort (a,b) -> x[a] - x[b]

sortedKeys
'a' : 1
'b' : 3
'c' : 4
'd' : -1



[ 'd', 'a', 'b', 'c' ]


[ 'd', 'a', 'b', 'c' ]






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

jVY9idq9l 7UjxKDsD6j,3wR,w aZ,jP uX Th3f3hBc,GXHPxV
J QYBDPTKnu0PDOJbKe4tmtwLGpBlnA7KKpX0rmV3R2 MskQeAPDc3XrWa4bbzdLe,WsYkQe 4FJk5jYJx,4n OklevGLvN RJA57

Popular posts from this blog

PySpark - SparkContext: Error initializing SparkContext File does not exist

django NoReverseMatch Exception

List of Kim Possible characters