4

Possible Duplicate:
Iterating hash based on the insertion order?

im trying to get the fifth element of this hash

%cds = ("Dr. Hook"         => "Sylvias Mother", 
        "Rod Stewart"    => "Maggie May", 
        "Andrea Bocelli"  => "Romanza",
        "Kenny Rogers"  => "For the good times",
        "Bee Gees"        => "One night only",
        "Bonnie Tyler"    => "Hide your heart");

In this case, it would be "bee Gees","One night only"

but when im trying to implement this code

while (($key, $value) = each %cds) {
   print "$key $value\n";
 }

this prints all the elements, but not in order

How this is possible and how i can get the fifth element?

Community
  • 1
  • 1
user1193803
  • 43
  • 1
  • 3
  • 5
    The answer is "don't use a hash". See useful answers on: http://stackoverflow.com/questions/1558625/how-can-i-maintain-the-order-of-keys-i-add-to-a-perl-hash – Alex Feb 07 '12 at 04:30
  • I don't known if perl has any collection like LinkedHashMap in Java. If it does, then problem solved. BTW, why do you care the order of the Map? – George Feb 07 '12 at 06:56
  • Note about 'duplicate' not allways is good aproach. 1st, perl hash is determined by searching as fast as possible given index. it can change order on every insert or remove element. second, each HASH iterator is still experimental, and it is not working stable. then replace each %cds by keys %cds . for small list you can get 5th element in sorted list (sort keys %cds)[5] . it is not effective for bugger hash tables. then you should replace first line 'while' by foreach $key (sort keys %cds) { then add second line $value=$cds{$key} . this will work stable. – Znik Nov 03 '20 at 07:30
  • maybe better will be organize your data set with sqlite? perl has got libraries for them – Znik Nov 03 '20 at 07:32

4 Answers4

5

Hashes are not stored in any (user sensible) order. If you need to get items by a numeric index, you shouldn't be using a hash. You could sort the collection of keys (still doesn't get "order of insertion").

John3136
  • 28,809
  • 4
  • 51
  • 69
4

One option is to keep your hash as-is (an unordered collection) and augment with an array of keys (an ordered sequence). Then loop over the array (in insertion order) and use the hash to look-up the corresponding values.

Another option is to download an ordered hash implementation from CPAN: Tie::Hash::Indexed

Hope this helps :-)

bvr
  • 9,687
  • 22
  • 28
Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
2
@cds = (["Dr. Hook",       "Sylvias Mother"], 
        ["Rod Stewart",    "Maggie May"], 
        ["Andrea Bocelli", "Romanza"],
        ["Kenny Rogers",   "For the good times"],
        ["Bee Gees",       "One night only"],
        ["Bonnie Tyler",   "Hide your heart"],
);

This is an array of arrays. $cd[4][0] is the artist, $cd[4][1] is the title.

Bill Ruppert
  • 8,956
  • 7
  • 27
  • 44
1

The clue is in the phrase "but not in order". A hash is not an ordered collection. If you want them in order, use an ordered collection, i.e. an array. Complete worked example:

#!/usr/bin/perl

use strict;
use warnings;

my @cds = (
    [ 'Dr. Hook'       => 'Sylvias Mother'     ],
    [ 'Rod Stewart'    => 'Maggie May'         ],
    [ 'Andrea Bocelli' => 'Romanza'            ],
    [ 'Kenny Rogers'   => 'For the good times' ],
    [ 'Bee Gees'       => 'One night only'     ],
    [ 'Bonnie Tyler'   => 'Hide your heart'    ],
);

for my $cd (@cds) {
    my ($key, $value) = @$cd;

    print "$key $value\n";
}

my $fifth_element = $cds[4];

print "Fifth element is @$fifth_element\n";
zgpmax
  • 2,777
  • 15
  • 22