Is it possible to iterate the hash in sorted order using while (my ($ key, $ value) ...) {} method?

For a hash of this format:

my $itemHash = { tag1 => { name => "Item 1", order => 1, enabled => 1, }, tag2 => { name => "Item 2", order => 2, enabled => 0, }, tag3 => { name => "Item 3", order => 3, enabled => 1, }, ... } 

I have this code that iterates through a hash correctly:

 keys %$itemHash; # Resets the iterator while(my($tag, $item) = each %$itemHash) { print "$tag is $item->{'name'}" } 

However, the order in which these elements are repeated seems rather random. Is it possible to use the same while format to iterate through them in the order indicated by the "order" key in the hash for each element?

(I know that I can sort the keys first and then loop through it. Just see if there is a cleaner way to do this.)

+4
source share
4 answers

The concept of an ordered hash is incorrect. Although the array is an ordered list of elements and therefore accessible by index, the hash is a (non-ordered) set of key-value pairs, where the keys are sets.

To complete your task, you will have to sort the keys by the order property:

 my @sorted = sort {$hash{$a}{order} <=> $hash{$b}{order}} keys %$itemHash; 

Then you can create key-value pairs via map :

 my @sortedpairs = map {$_ => $itemHash->{$_}} @sorted; 

We could wrap this in a sub:

 sub ridiculousEach { my %hash = @_; return map {$_ => $hash{$_}} sort {$hash{$a}{order} <=> $hash{$b}{order}} keys %hash; } 

to get a list of elements of key value with even size

 sub giveIterator { my %hash = @_; my @sorted = sort {$hash{$a}{order} <=> $hash{$b}{order}} keys %hash; return sub { my $key = shift @sorted; return ($key => $hash{$key}); }; } 

to create a callback that is drop down for each.

Then we can:

 my $iterator = giveIterator(%$itemHash); while (my ($tag, $item) = $iterator->()) { ...; } 

There is a serious flaw in this approach: each uses only two elements at a time and therefore works in read-only memory. This solution should read the entire hash and store an array of all keys. Minor with small hashes, this can become important with a very large number of elements.

+7
source

The order in which the keys to the hash is undefined. Therefore, you will need to sort the keys. One way would be, as you said, to pull out the keys and sort them, then scroll the keys.

Another way would be to sort them on the fly. I'm not sure if you think it is cleaner. Sort of:

 for my $key ( sort { $itemHash->{$a}{order} <=> $itemhash->{$b}{order} } keys %$itemHash ) { print "$key is $itemHash->{$key}{name}"; } 
+5
source

You can do something like:

 foreach my $key (sort keys %{$itemHash}) { print "$key : " . $itemHash->{$key}{name} . "\n"; } 
+4
source

It will be pretty clean. we must use cmp to sort the lines.

 my $itemHash = { tag1 => { name => "Item 1", order => 1, enabled => 1, }, tag2 => { name => "Item 2", order => 2, enabled => 0, }, tag3 => { name => "Item 3", order => 3, enabled => 1, } }; foreach((sort{$a cmp $b}(keys(%$itemHash)))){ print "$_ is $itemHash->{$_}->{'name'}\n"; } 
+2
source

Source: https://habr.com/ru/post/1436258/


All Articles