简体   繁体   中英

Processing a perl hash in sorted order by key

I have a Perl script that uses a hash where the contents are UUIDs and the keys are XML text elements. The XML text elements sort reasonably with an ordinary sort.

As I process each element, I write to a log file. The relevant code looks something like:

use 5.016;
open(my $loghandle, '>', 'process.log') or die "Couldn't open logfile";

%XMLhash = (
      'e01af02f-e8e5-476e-a250-e70c3925463a' => '<Form><AUni ws="fr">retirer</AUni></Form>',
      '8187b534-a8c8-4e14-bf0b-1bd9cfada31e' => '<Form><AUni ws="fr">pencher</AUni></Form>',
      '1848c7a2-c2f8-4884-8327-c0f1a9da7a4b' => '<Form><AUni ws="en">repair</AUni></Form>',
      'b36c127c-91b2-41db-96ec-18c172ad9917' => '<Form><AUni ws="fr">fou</AUni></Form>',
      'abb4e9dc-dc66-43b5-951e-e720e21da286' => '<Form><AUni ws="en">four</AUni></Form>',
      '8cf707e6-e0a1-4611-bb28-ff18d2de38db' => '<Form><AUni ws="en">robust</AUni></Form>',
    );

while( my( $guid, $XMLtext ) = each %XMLhash ) {
    my $somestuff = 'What I did', # some process that tells what I did 
    say $loghandle "Text:$XMLtext Guid:$guid I did $somestuff";
    }

And the log file it produces (possibly different order each run):

Text:<Form><AUni ws="en">robust</AUni></Form> Guid:8cf707e6-e0a1-4611-bb28-ff18d2de38db I did:What I did
Text:<Form><AUni ws="en">four</AUni></Form> Guid:abb4e9dc-dc66-43b5-951e-e720e21da286 I did:What I did
Text:<Form><AUni ws="fr">retirer</AUni></Form> Guid:e01af02f-e8e5-476e-a250-e70c3925463a I did:What I did
Text:<Form><AUni ws="fr">fou</AUni></Form> Guid:b36c127c-91b2-41db-96ec-18c172ad9917 I did:What I did
Text:<Form><AUni ws="en">repair</AUni></Form> Guid:1848c7a2-c2f8-4884-8327-c0f1a9da7a4b I did:What I did
Text:<Form><AUni ws="fr">pencher</AUni></Form> Guid:8187b534-a8c8-4e14-bf0b-1bd9cfada31e I did:What I did

I would like the log file to be sorted by the XML text, ie, the key of the hash.

I tried changing the while statement to:

while( my( $guid, $XMLtext ) = each sort keys %XMLhash ) {

That gave me an error: "Type of argument to each on reference must be unblessed hashref or arrayref at ..."

To have a sorted log file, can I process the hash in order by key?

In my current work-around, I write the logs to an array, sort that array and write the array to the log file. That seems to me to be sub-optimal.

Don't use each for that. It only works on hashes (and arrays in newer Perls). Use a regular foreach and sort by value.

foreach my $key ( sort { $xml{$a} cmp $xml{$b} } keys %xml ) {
  say "$key: $xml{$key}";
}

You need to grab the keys and then sort on the values because that's where the XML thingy is.

If you wanted to sort by the text node first, and individual text nodes by the ws attribute, it becomes more complex.

foreach my $key ( map { $_->[0] }
                  sort { $a->[2] cmp $b->[2] || $a->[1] cmp $b->[1] }
                  map { [ $_, $xml{$_} =~ m/"(..)">([^<]+)</ ] } keys %xml
) {
  say "$key: $xml{$key}";
}

This makes use of the so-called Schwartzian transform , which performs an expensive operation once, memoizes the result to sort on that, and then put the original data back in place. Since your XML snippet is always of the same format, it is save to use a regular expressions to extract the data to sort on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM