2

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;
  });
});
Majid Fouladpour
  • 29,356
  • 21
  • 76
  • 127
  • 5
    Your Arrays are in an Object, and Objects have no order. Are you looking to end up with an Array of Arrays instead? – I Hate Lazy Nov 03 '12 at 00:01
  • 1
    http://stackoverflow.com/questions/1069666/sorting-javascript-object-by-property-value – Ana Nov 03 '12 at 00:05
  • @user1689607, you are correct! I tested this and here is the result: http://jsbin.com/ehinec/1/ but, I don't know why using Firebug, the order of console.log entries implies the objects *do have* order. – Majid Fouladpour Nov 03 '12 at 00:12
  • @Ana, that is close, and I checked it before posting this question, but the problems are different. – Majid Fouladpour Nov 03 '12 at 00:13
  • 2
    @MajidFouladpour: To be accurate, I should say that the `for-in` enumeration of objects has no required order by the ECMAScript spec. It's up to the implementation what order should be used. If a spec wants to enumerate object properties differently every time, they may. The key point is that order must be enforced by your code. This usually involves Arrays since you can enforce order via a numeric iteration. – I Hate Lazy Nov 03 '12 at 00:16
  • http://stackoverflow.com/a/5467142/139010 – Matt Ball Nov 03 '12 at 00:51

5 Answers5

3

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.

I Hate Lazy
  • 47,415
  • 13
  • 86
  • 77
0

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});
Rene Koch
  • 1,252
  • 10
  • 24
  • 1
    the guy want to sort and Object - which is not possible, Object can not be sorted. Your example is useful but it is Array related. – Reflective Nov 03 '12 at 00:11
0

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() { ... }
Reflective
  • 3,854
  • 1
  • 13
  • 25
0

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>';
});
Majid Fouladpour
  • 29,356
  • 21
  • 76
  • 127
  • It turns out I have asked the wrong question, and a similar answer to this one would not be possible without the added clarification of the intent. I am not going to mark this answer the accepted one then. – Majid Fouladpour Nov 03 '12 at 00:56
0

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

Bruno
  • 5,772
  • 1
  • 26
  • 43