Sorting an object by value

2019-03-03 05:51发布

问题:

I'm having difficulty with this using Underscore. I'd like to sort the below object alphabetically by its value, and return it in the same format.

{
  "accommodation" : "Accommodation",
  "bed-breakfast" : "Bed & Breakfast",
  "caravan-camping" : "Caravan & Camping",
  "cottages" : "Cottages",
  "friends-family" : "Friends & Family",
  "health-spas" : "Health Spas",
  "hostels" : "Hostels",
  "hotels" : "Hotels",
  "self-catering" : "Self Catering",
  "backpacking" : "Backpacking",
  "car-touring" : "Car Touring"
}

I've managed to sort it but it replaces the key with an index value. Any ideas? This should be fairly straight forward. What am I missing?

Thanks in advance!

回答1:

You can't reliably sort a JavaScript object. ES5 section 12.6.4 says:

The mechanics and order of enumerating the properties (step 6.a in the first algorithm, step 7.a in the second) is not specified.

Section 15.2.3.7 says:

If an implementation defines a specific order of enumeration for the for-in statement, that same enumeration order must be used to order the list elements in step 3 of this algorithm.

Section 15.2.3.14 (Object.keys) says:

If an implementation defines a specific order of enumeration for the for-in statement, that same enumeration order must be used in step 5 of this algorithm.

You can also look at section 8.6 The Object Type to see if there is any mention of property order (hint: there isn't). So in ES5 the property order is implementation dependent and the only restriction is, more or less, that if there is a defined order anywhere then that order has to be the same everywhere.

The ES6 draft contains similar language in section 9.1.11:

The mechanics and order of enumerating the properties is not specified but must conform to the rules specified below.

So there is absolutely no portable and reliable order to things in an Object.

And no, you can't go through JSON either as JSON defines an object as:

... an unordered set of name/value pairs

Some JavaScript implementations will order Objects by insertion order but you can't rely on that.

The best you can do would be to convert your Object to an array-of-arrays with something like this:

var sorted = _(obj).chain()
                   .pairs()
                   .sortBy(_.last)
                   .value();

or an array-of-objects:

var sorted2 = _(obj).chain()
                    .pairs()
                    .sortBy(_.last)
                    .map(function(a) { return _([a]).object() })
                    .value();

Demo: http://jsfiddle.net/ambiguous/76ZPx/



回答2:

Thanks to 'mu is too short's answer, it has got me most of the way. However, it needs to be converted back to an object, and after some fiddling around, It's very simple. Here's the complete answer:

var sortedDataArr = _(data).chain()
                           .pairs()
                           .sortBy(_.last)
                           .value();

var sortedDataObj = _.object( sortedDataArr );


回答3:

A JS object has no order. You can't programatically sort an object. Only arrays

Not really. When you call JSON.stringify on an object, the order of properties in the output string is the order in which the properties were defined on that object. The same applies to Object.keys(obj) and for(var property in obj) (assuming obj is your object). You can do that:

var originalObject = {
  "accommodation" : "Accommodation",
  "bed-breakfast" : "Bed & Breakfast",
  "caravan-camping" : "Caravan & Camping",
  "cottages" : "Cottages",
  "friends-family" : "Friends & Family",
  "health-spas" : "Health Spas",
  "hostels" : "Hostels",
  "hotels" : "Hotels",
  "self-catering" : "Self Catering",
  "backpacking" : "Backpacking",
  "car-touring" : "Car Touring"
};
// map your object onto an array [key, value]
var arr = _.map(originalObject, function(value, key) {
    return [key, value]; 
});
// sort it by originalObject's values
arr.sort(function(a, b) {
    return a[1].localeCompare(b[1]); 
});
// create an object from an array
var obj = _.object(arr);

JSON.stringify(originalObject) yields:

"{"accommodation":"Accommodation","bed-breakfast":"Bed & Breakfast","caravan-camping":"Caravan & Camping","cottages":"Cottages","friends-family":"Friends & Family","health-spas":"Health Spas","hostels":"Hostels","hotels":"Hotels","self-catering":"Self Catering","backpacking":"Backpacking","car-touring":"Car Touring"}"

JSON.stringify(obj) yields:

"{"accommodation":"Accommodation","backpacking":"Backpacking","bed-breakfast":"Bed & Breakfast","car-touring":"Car Touring","caravan-camping":"Caravan & Camping","cottages":"Cottages","friends-family":"Friends & Family","health-spas":"Health Spas","hostels":"Hostels","hotels":"Hotels","self-catering":"Self Catering"}"