How to sort a Javascript object, or convert it to an array?

55,202

Solution 1

Array.prototype.slice.call(arrayLikeObject)

is the standard way to convert and an array-like object to an array.

That only really works for the arguments object. To convert a generic object to an array is a bit of a pain. Here's the source from underscore.js:

_.toArray = function(iterable) {
    if (!iterable)                return [];
    if (iterable.toArray)         return iterable.toArray();
    if (_.isArray(iterable))      return iterable;
    if (_.isArguments(iterable))  return slice.call(iterable);
    return _.values(iterable);
};

_.values = function(obj) {
    return _.map(obj, _.identity);
};

Turns out you're going to need to loop over your object and map it to an array yourself.

var newArray = []
for (var key in object) {
    newArray.push(key);
}

You're confusing the concepts of arrays and "associative arrays". In JavaScript, objects kind of act like an associative array since you can access data in the format object["key"]. They're not real associative arrays since objects are unordered lists.

Objects and arrays are vastly different.

An example of using underscore:

var sortedObject = _.sortBy(object, function(val, key, object) {
    // return an number to index it by. then it is sorted from smallest to largest number
    return val;
});

See live example

Solution 2

You should be able to convert a JavaScript object into an array like so...

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

var arr = [];

for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      arr.push(obj[key]);  
    }
}

console.log(arr); // ["a", "b", "c"]

See it on jsFiddle.

Solution 3

If your JavaScript object is an array-like object, that is, an Object instance with a valid numerical length property, then you can directly use many native Array methods on it thanks to the call method. For example:

// Sorts the given objet in-place as if it was an array
Array.prototype.sort.call(yourObject);

So if you know the number of entries to be sorted (How to efficiently count the number of keys/properties of an object in JavaScript?), you can do:

yourObject.length = theNumberOfEntries;
Array.prototype.sort.call(yourObject);
// Optionally: delete yourObject.length;

Note that this will only sort properties indexed by "0", "1", "2", ... to length - 1 inclusive, like in an Array. The object's other properties will not be re-ordered.

Solution 4

Most of these answers over-complicate the issue or use JQuery or Underscore whereas the OP never asked for those.

You can convert an object to an array like this:

myArray= Object.keys(data).map(function(key) { return data[key] });

And sort the result like this:

myArray.sort(function(x, y) {return x.level - y.level});

If you need the id/index, then you need to do a bit more:

Object.keys(data).map(function(key) { 
  var obj = data[key];
  obj.index = key;
  return obj 
});

Solution 5

I have stumbled upon that problem recently while trying to group an array of objects by one of it's properties, resulting in one object I could therefore not sort.

Concretely it's an array of blog posts I wanted to group by year and have them sorted by descending years. I used underscore's utility :

var grouped = _.groupBy(blogposts, function(post){
  var date = new Date(post.publication_date)
  return date.getFullYear()
})
//=> { 2010: [blogpost, blogpost,etc], 2011: [blogpost, etc] }

As @Raynos explained I had to end up with some sort of array first before sorting it...

It turns out underscore (1.4) has a nice little utility called pairs which will map the {key: value} of your object in an array of [key, value]. The equivalent of :

var paired = _.map(grouped, function(val, key){
  return [key, val]
})
//=> [ [2010, [blogpost, blogpost, ...] ], [2011, [blogpost, blogpost, ...]]...]

From there on you can easily sort by the first term of each pair.

Here's the end result:

var grouped = _.groupBy(result.resource, function(resource){
  var date = new Date(resource.pub_date)
  return date.getFullYear() //+ "." + (date.getMonth()+1)
})

var paired = _.pairs(grouped)

var sorted = _.sortBy(paired, function(pairs){
  return -parseInt(pairs[0])
})

return sorted;
// Giving me the expected result:
//=> [ [2013, [blogpost, blogpost, ...] ], [2012, [blogpost, blogpost, ...]]...]

I'm sure though there's a better and more performant way, but coming from ruby this code is immediately understandable for me.

Share:
55,202
Questioner
Author by

Questioner

Updated on March 08, 2020

Comments

  • Questioner
    Questioner about 4 years

    I have some JSON data that I get from a server. In my JavaScript, I want to do some sorting on it. I think the sort() function will do what I want.

    However, it seems that JavaScript is converting the JSON data into an Object immediately on arrival. If I try to use the sort() method, I get errors a-plenty (using Firebug for testing).

    I've looked around the net, and everyone seems to say that for one thing, JSON objects are already JavaScript arrays, and also that Objects can be treated just like arrays. Like over on this question, where in one of the answers, a guy says "The [Object object] is your data -- you can access it as you would an array."

    However, that is not exactly true. JavaScript won't let me use sort() on my object. And since the default assumption is that they're all the same thing, there don't seem to be any instructions anywhere on how to convert an Object to an Array, or force JavaScript to treat it as one, or anything like that.

    So... how do I get JavaScript to let me treat this data as an array and sort() it?

    Console log output of my object looks like this (I want to be able to sort by the values in the "level"):

    OBJECT JSONdata

    { 
    1: {
        displayName: "Dude1",
        email: "[email protected]<mailto:[email protected]>",
        lastActive: 1296980700, 
        level: 57, 
        timeout: 12969932837
    }, 2: {
        displayName: "Dude2",
        email: "[email protected]<mailto:[email protected]>",
        lastActive: 1296983456,
        level: 28,
        timeout: 12969937382
    }, 3: {
        displayName: "Dude3",
        email: "[email protected]<mailto:[email protected]>",
        lastActive: 1296980749,
        level: 99,
        timeout: 129699323459
    } 
    }