Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating over a JavaScript object in sort order based on particular key value of a child object

Short version: I'm looking for the JavaScript equivalent of Perl's

for my $key ( sort { $hash{$a}{foo} cmp $hash{$b}{foo} } keys %hash ) {
    # do something with $key
}

More detail:

I have a JSON object which consists of a bunch of other JSON objects which have identical properties to each other, like a hash of hashes in Perl: eg:

var peopleobj = { 
    "0291" : { "Forename" : "Jeremy", "Surname" : "Dyson" },
    "0398" : { "Forename" : "Billy", "Surname" : "Bunter" },
    "6714" : { "Forename" : "Harry", "Surname" : "Peterson" },
    "9080" : { "Forename" : "Barry", "Surname" : "Mainwaring"}
}

I want to iterate through the objects in peopleobj in order of the surname values, eg to print out the names in surname order. Plain JavaScript or jQuery solutions will work in the context in which this is being deployed.

Thanks in advance for your valuable time.

like image 852
Jonah Avatar asked Oct 28 '10 10:10

Jonah


1 Answers

Interesting question... One plain JavaScript solution is to create an index for your objects in a separate array, based on the 'Surname' property. Something like this1:

var peopleobj = { 
   "0291" : { "Forename" : "Jeremy", "Surname" : "Dyson" },
   "0398" : { "Forename" : "Billy", "Surname" : "Bunter" },
   "6714" : { "Forename" : "Harry", "Surname" : "Peterson" },
   "9080" : { "Forename" : "Barry", "Surname" : "Mainwaring" }
};

var index = [];

// build the index
for (var x in peopleobj) {
   index.push({ 'key': x, 'Surname': peopleobj[x]['Surname'] });
}

// sort the index
index.sort(function (a, b) { 
   var as = a['Surname'], 
       bs = b['Surname']; 

   return as == bs ? 0 : (as > bs ? 1 : -1); 
}); 

Now you would be able to iterate over your index array:

for (var i = 0; i < index.length; i++) {
   console.log(peopleobj[index[i]['key']]['Surname']);
}

Result (Tested in Firebug console):

Bunter
Dyson
Mainwaring
Peterson

You may want to wrap this up into some sort of reusable Iterator object, even though it would be difficult to get as terse as Perl:

// Our reusable Iterator class:
function MyIterator (o, key) {
   this.index = [];
   this.i = 0;
   this.o = o;

   for (var x in o) {
      this.index.push({ 'key': x, 'order': o[x][key] });
   }

   this.index.sort(function (a, b) { 
      var as = a['order'], 
          bs = b['order']; 

      return as == bs ? 0 : (as > bs ? 1 : -1); 
   }); 

   this.len = this.index.length;
}

MyIterator.prototype.next = function () {
   return this.i < this.len ?
          this.o[this.index[this.i++]['key']] :
          null;
};

Then use it as follows:

// Our JavaScript object:
var peopleobj = { 
   "0291" : { "Forename" : "Jeremy", "Surname" : "Dyson" },
   "0398" : { "Forename" : "Billy", "Surname" : "Bunter" },
   "6714" : { "Forename" : "Harry", "Surname" : "Peterson" },
   "9080" : { "Forename" : "Barry", "Surname" : "Mainwaring" }
};

// Build the Iterator object, using the 'Surname' field:
var surnameIter = new MyIterator(peopleobj, 'Surname');

// Iterate:
var i;

while (i = surnameIter.next()) {
   console.log(i['Surname'] + ' ' + i['Forename']);
}

Result:

Bunter Billy
Dyson Jeremy
Mainwaring Barry
Peterson Harry

1 You may want to use the hasOwnProperty() method to ensure that the properties belong to your object and are not inherited from Object.prototype:

for (var x in peopleobj) {
   if (peopleobj.hasOwnProperty(x)) {
      index.push({ 'key': x, 'Surname': peopleobj[x]['Surname'] });
   }
}
like image 142
Daniel Vassallo Avatar answered Oct 06 '22 00:10

Daniel Vassallo