113

Let's say I have a Javascript associative array (a.k.a. hash, a.k.a. dictionary):

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;

How can I iterate over the keys in sorted order? If it helps simplify things, I don't even need the values (they're all just the number 1).

mike
  • 46,876
  • 44
  • 102
  • 112
  • 11
    why are you using the new Array() construct and then using it like an object? – Luke Schafer May 21 '09 at 00:21
  • @Luke.. I did this at first too, coming from a PHP background. I've learnt now though :) – alex May 21 '09 at 01:24
  • 22
    @Luke: 'cause i'm inexperienced, it seems. Can you post the correct way in an answer? – mike May 21 '09 at 16:49
  • 4
    You can simply create any object. In Javascript there's no difference between a dictionary/"named array" and a regular object. You can therefore access a['b'] with a.b and vice versa. The shortest way to create an object is `a = {};`. – Lodewijk Jan 18 '12 at 21:29

10 Answers10

137

You can use the Object.keys built-in method:

var sorted_keys = Object.keys(a).sort()

(Note: this does not work in very old browsers not supporting EcmaScript5, notably IE6, 7 and 8. For detailed up-to-date statistics, see this table)

molnarg
  • 2,775
  • 1
  • 19
  • 20
123

You cannot iterate over them directly, but you can find all the keys and then just sort them.

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;    

function keys(obj)
{
    var keys = [];

    for(var key in obj)
    {
        if(obj.hasOwnProperty(key))
        {
            keys.push(key);
        }
    }

    return keys;
}

keys(a).sort(); // ["a", "b", "z"]

However there is no need to make the variable 'a' an array. You are really just using it as an object and should create it like this:

var a = {};
a["key"] = "value";
Petrus Theron
  • 27,855
  • 36
  • 153
  • 287
Matthew
  • 3,086
  • 2
  • 20
  • 9
14

you could even prototype it onto object:

Object.prototype.iterateSorted = function(worker)
{
    var keys = [];
    for (var key in this)
    {
        if (this.hasOwnProperty(key))
            keys.push(key);
    }
    keys.sort();

    for (var i = 0; i < keys.length; i++)
    {
        worker(this[ keys[i] ]);
    }
}

and the usage:

var myObj = { a:1, b:2 };
myObj.iterateSorted(function(value)
{
    alert(value);
} 
oers
  • 18,436
  • 13
  • 66
  • 75
Luke Schafer
  • 9,209
  • 2
  • 28
  • 29
  • 3
    I upvoted this answer it seemed quite good, but it turned out it breaks jquery :( http://stackoverflow.com/questions/1827458/prototyping-object-in-javascript-breaks-jquery and in general is considered a very bad idea "You should never extend Object.prototype. It does far more than break jQuery; it completely breaks the "object-as-hashtables" feature of Javascript. Don't do it. You can ask John Resig, and he'll tell you the same thing." – msanjay Dec 14 '12 at 18:20
  • You know what? I hate prototypes too :) I never use them and actively discourage their use. I sort of felt this way 3.5 years ago when I wrote this answer, but suggested it anyway... thanks for providing the info. As an aside, it SHOULDN'T break frameworks as they should ALWAYS use hasOwnProperty when iterating objects – Luke Schafer Dec 20 '12 at 22:54
  • Here is an example using the [values instead of they keys for sorting](http://stackoverflow.com/a/29394249/99923) while still maintaining the `key -> value` relationship. – Xeoncross Apr 01 '15 at 15:03
6

I agree with Swingley's answer, and I think it is an important point a lot of these more elaborate solutions are missing. If you are only concerned with the keys in the associative array and all the values are '1', then simply store the 'keys' as values in an array.

Instead of:

var a = { b:1, z:1, a:1 };
// relatively elaborate code to retrieve the keys and sort them

Use:

var a = [ 'b', 'z', 'a' ];
alert(a.sort());

The one drawback to this is that you can not determine whether a specific key is set as easily. See this answer to javascript function inArray for an answer to that problem. One issue with the solution presented is that a.hasValue('key') is going to be slightly slower than a['key']. That may or may not matter in your code.

Community
  • 1
  • 1
Grant Wagner
  • 25,263
  • 7
  • 54
  • 64
3

There's no concise way to directly manipulate the "keys" of a Javascript object. It's not really designed for that. Do you have the freedom to put your data in something better than a regular object (or an Array, as your sample code suggests)?

If so, and if your question could be rephrased as "What dictionary-like object should I use if I want to iterate over the keys in sorted order?" then you might develop an object like this:

var a = {
  keys : new Array(),
  hash : new Object(),
  set : function(key, value) {
    if (typeof(this.hash[key]) == "undefined") { this.keys.push(key); }
    this.hash[key] = value;
  },
  get : function(key) {
    return this.hash[key];
  },
  getSortedKeys : function() {
    this.keys.sort();
    return this.keys;
  }
};

// sample use
a.set('b',1);
a.set('z',1);
a.set('a',1);
var sortedKeys = a.getSortedKeys();
for (var i in sortedKeys) { print(sortedKeys[i]); }

If you have no control over the fact that the data is in a regular object, this utility would convert the regular object to your fully-functional dictionary:

a.importObject = function(object) {
  for (var i in object) { this.set(i, object); }
};

This was a object definition (instead of a reusable constructor function) for simplicity; edit at will.

Travis Wilson
  • 949
  • 9
  • 19
2

You can use the keys function from the underscore.js library to get the keys, then the sort() array method to sort them:

var sortedKeys = _.keys(dict).sort();

The keys function in the underscore's source code:

// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) {
    if (obj !== Object(obj)) throw new TypeError('Invalid object');
    var keys = [];
    for (var key in obj) if (_.has(obj, key)) keys.push(key);
    return keys;
};    

// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
    return hasOwnProperty.call(obj, key);
};
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
2

Get the keys in the first for loop, sort it, use the sorted result in the 2nd for loop.

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;

var b = [];
for (k in a) b.push(k);
b.sort();
for (var i = 0; i < b.length; ++i) alert(b[i]);
pts
  • 80,836
  • 20
  • 110
  • 183
0

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;


var keys=Object.keys(a).sort();
for(var i=0,key=keys[0];i<keys.length;key=keys[++i]){
  document.write(key+' : '+a[key]+'<br>');
}
Vlad V
  • 109
  • 1
  • 6
0

I really like @luke-schafer's prototype idea, but also hear what he is saying about the issues with prototypes. What about using a simple function?

function sortKeysAndDo( obj, worker ) {
  var keys = Object.keys(obj);
  keys.sort();
  for (var i = 0; i < keys.length; i++) {
     worker(keys[i], obj[keys[i]]);
  }
}

function show( key, value ) {
  document.write( key + ' : ' + value +'<br>' );
}

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;

sortKeysAndDo( a, show);

var my_object = { 'c': 3, 'a': 1, 'b': 2 };

sortKeysAndDo( my_object, show);

This seems to eliminate the issues with prototypes and still provide a sorted iterator for objects. I am not really a JavaScript guru, though, so I'd love to know if this solution has hidden flaws I missed.

EFC
  • 1,890
  • 18
  • 39
0
<script type="text/javascript">
    var a = {
        b:1,
        z:1,
        a:1
    }; // your JS Object
    var keys = [];
    for (key in a) {
        keys.push(key);
    }
    keys.sort();
    var i = 0;
    var keyslen = keys.length;
    var str = '';
    //SORTED KEY ITERATION
    while (i < keyslen) {
        str += keys[i] + '=>' + a[keys[i]] + '\n';
        ++i;
    }
    alert(str);
    /*RESULT:
    a=>1
    b=>1
    z=>1
    */
</script>
Fran Corpier
  • 2,871
  • 1
  • 16
  • 10