开发者

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

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: "dude1@example.com<mailto:dude1@example.com>",
    lastActive: 1296980700, 
    level: 57, 
    timeout: 12969932837
}, 2: {
    displayName: "Dude2",
    email: "dude2@example.com<mailto:dude2@example.com>",
    lastActive: 1296983456,
    level: 28,
    timeout: 12969937382
}, 3: {
    displayName: "Dude3",
    email: "dude3@example.com<mailto:dude3@example.com>",
    lastActive: 1296980749,
    level: 99,
    timeout: 129699323459
} 
}


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


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.


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.


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


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.


jQuery offers a map function, which will iterate through each element in an array or object and map the results into a new array.

Prior to jQuery 1.6, $.map() supported traversing arrays only.

We can use this to convert any object to an array as follows...

  myArray = $.map(myObject, function (el) {
    return el;
  });

But... if the callback function returns null or undefined, then that value is removed from the array, in most cases this is useful, but it can cause problems if you need null values in myArray.

jQuery offers a solution for this... return the value as an array with the single value

myArrayWithNulls = jQuery.map(myObject, function (el) {
  return [el];
});

Here's a fiddle demonstrating the two approaches: http://jsfiddle.net/chim/nFyPE/

http://jsperf.com/object-to-array-jquery-2


I wrote a small function to recursively convert an object with properties that may also be objects to a multi-dimensional array. This code is dependent on underscore or lodash for the forEach and toArray methods.

function deepToArray(obj) {
    var copy = [];


    var i = 0;
    if (obj.constructor == Object ||
        obj.constructor == Array) {

        _.forEach(obj, process);

    } else {

        copy = obj;

    }


    function process(current, index, collection) {

        var processed = null;
        if (current.constructor != Object &&
            current.constructor != Array) {
            processed = current;
        } else {
            processed = deepToArray(_.toArray(current));
        }

        copy.push(processed);

    }

    return copy;
}

Here is the fiddle: http://jsfiddle.net/gGT2D/

Note: This was written to convert an object that was originally an array back into an array, so any non-array index key values will be lost.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜