Majid Fouladpour
Majid Fouladpour

Reputation: 30262

Sort javascript object by property value

How would one sort the following object to correct alphabetical order of names?

var users_by_id = {
   '12': ['Ted', 34, 'Male'],
   '13': ['Ben', 36, 'Male'],
   '14': ['Alice', 42, 'Female']
}

var users_by_name = {
   '14': ['Alice', 42, 'Female'],
   '13': ['Ben', 36, 'Male'],
   '12': ['Ted', 34, 'Male']
}

I have a plan which would need two passes over the object, but I am not sure if there is a simpler way to do it. The plan I have is this (I am using jQuery):

var users_by_name = {};
var names = [];
$.each(users_by_id, function(id, props) {
  names.push(props[0]);
});
names.sort();
$.each(names, function(i, n) {
  $.each(users_by_id, function(id, props) {
    if(n == props[0]) users_by_name[id] = props;
  });
});

Upvotes: 2

Views: 1489

Answers (5)

Bruno
Bruno

Reputation: 5822

Another option that makes use of Array.prototype.sort function

var users_by_id = {
       '0': ['Ted', 34, 'Male'],
       '1': ['Ben', 36, 'Male'],
       '2': ['Alice', 42, 'Female']
 }

// as long as length is bigger than max object key value
users_by_id.length = Object.keys( users_by_id ).length; 

Array.prototype.sort.call( users_by_id, function( a, b ) {

        var aName = a[0].toLowerCase();
        var bName = b[0].toLowerCase();

        if( aName > bName ) return 1;
        if( aName < bName ) return -1;
        return 0;
});

There are a few caveats to the above:

  1. The object must contain a length property
  2. The length property must have a value larger than the max value of the object keys

If you do not use contiguous numbers as keys and do the following:

   var users_by_id = {
           '12': ['Ted', 34, 'Male'],
           '13': ['Ben', 36, 'Male'],
           '14': ['Alice', 42, 'Female']
     }

Once sorted the object's sorted numeric keys will be renamed => 0,1,2

Take a look at the fiddle here

Upvotes: 0

Majid Fouladpour
Majid Fouladpour

Reputation: 30262

OK, I got it. As has been pointed in the comments, Objects do not have any order. So no matter in what order the members are added (or defined), iterating over the object will produce the same result.

So, I should change my approach. A bit of background is neccessary to see why the new method would work (which is missing in the question by the way).

I have a users table and I want to display an html table with users in alphabetical order like so:

<tr id="user-N"><td>NAME</td><td>AGE</td><td>GENDER</td></tr>

And here is what I came by:

var name_ids = [];
var trows = '', thisNID;
$.each(users_by_id, function(id, props) {
  name_ids.push(props[0]+':'+id);
});
name_ids.sort();
$.each(name_ids, function(i,nid) {
  thisNID = nid.split(':');
  trows += '<tr id="user-' + thisNID[1] + '">';
  trows += '<td>' + users_by_id[thisNID[1]][0] + '</td>';
  trows += '<td>' + users_by_id[thisNID[1]][1] + '</td>';
  trows += '<td>' + users_by_id[thisNID[1]][2] + '</td>';
  trows += '</tr>';
});

Upvotes: 0

Reflective
Reflective

Reputation: 3917

Sorting is usually made when you have to iterate on a collection. So... my suggestion is to ad to methods to your object's prototype - .keysById and .keysByName which have to return keys ordered by the condition you mentioned above. So when you have to iterate by name you can do:

var kbn = yourObj.keysByName(); 
for(var i=0; i<yourObj.length;i++) { 
  ... yourObj[kbn[i]] ....  
}

You can do this methods, i'm sure, so I won't give examples.

yourObj.prototype.keysByName = function() { ... }

Upvotes: 0

I Hate Lazy
I Hate Lazy

Reputation: 48789

If you want to keep your Object based storage instead of using an Array, you'll need to create Arrays of the properties of the Object, and sort that to get an order.

var users = {
   '12': ['Ted', 34, 'Male'],
   '13': ['Ben', 36, 'Male'],
   '14': ['Alice', 42, 'Female']
};

var props = Object.keys(users);

var user_props_by_name = props.slice().sort(function(prop_a, prop_b) {
    return users[prop_a][0].localeCompare(users[prop_b][0]);
});

var user_props_by_age = props.slice().sort(function(prop_a, prop_b) {
    return users[prop_a][1] - users[prop_b][1];
});

Then you can iterate the Array and use the property names to look up items in the Object.

user_props_by_name.forEach(function(prop) {
    console.log(users[prop]);
});

user_props_by_age.forEach(function(prop) {
    console.log(users[prop]);
});

This will take some maintenance though when adding and removing users. You would probably want to create a layer that adds and removes users, and updates the sort Arrays at the same time.

Upvotes: 3

Rene Koch
Rene Koch

Reputation: 1291

sort takes an optional parameter which is a compare function https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort

var data =[
  ['Ted', 34, 'Male'],
  ['Ben', 36, 'Male'],
  ['Alice', 42, 'Female']
];

var sortbyid = data.sort(function(a,b){return a[1]<b[1] ? -1 : a[1]>b[1] ? 1 : 0});
var sortbyname = data.sort(function(a,b){return a[0]<b[0] ? -1 : a[0]>b[0] ? 1 : 0});

Upvotes: 0

Related Questions