views:

62

answers:

1

Hello all

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.

+4  A: 

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'] });
   }
}
Daniel Vassallo
+1. Good answer, although the comparison function passed to `sort()` is supposed to return a number, not a Boolean. I'd suggest something like `function (a, b) { var as = a.Surname., bs = b.Surname; return as == bs ? 0 : (as > bs ? 1 : -1); }`
Tim Down
@Tim: Thanks, you're right. I'll update my answer.
Daniel Vassallo