Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Hash Insertion order without IxHash

Status
Not open for further replies.

PerlGerl

Programmer
Jun 29, 2004
19
US
Does anyone know how i would go about retreiving from a hash in insertion order without using the tie::IxHash module???
 
as in, what application? Can you give an example?

___________________________________
[morse]--... ...--[/morse], Eric.
 
I have a Hash given by {key1,value1,key2,value2,key3,value3}
and I am calling to print the hash as such
key1: value1
key2: value2
key3: value3

As you know, when you access a hash, unlike an array the items are not ordered so it is outputed like this
key2: value2
key1: value1
key3: value3

I want to print them out in the insertion order (first example) The way I found to do is is using the tie::Ixhash module, I am looking for another way because this module is not standard and It is going to be a pain to port the script.

Let me know if you need actual code or if this explaination is suitable.
 
Either of these may work - you could populate an array with the hash keys as you enter them into the hash.
Code:
my (@array, %hash);
while (<>) {
    my ($key, $val) = split(',', $_);
    $hash{$key} = $val;
    push @array, $key;
}

foreach (@array) {
    print "$_ - $hash{$_}";
}

You can then iterate over the array to get the order that the values were inserted.

Alternately, you could do something like the followign - a second hash which the keys will be generated by a counter that's incremented every time a value is inserted.
Code:
my (%order, %hash, $counter);

while (<>) {
    my ($key, $val) = split ',', $_;
    $order{++$counter} = $key;
    $hash{$key} = $val;
}

foreach (sort {$a <=> $b} keys %order) {
    print "$order{$_} - $hash{$order{$_}}";
}
 
Interestingly enough, while this made the output in a different order, it is not in insertion order. (Strange I know) here is my code

Code:
my (%order, %hash, $counter);
@data = split (/,/, $text);
my %data = ( @data ); 
while( my($key, $val) = each(%data) ) {
	$order{++$counter} = $key;
    $hash{$key} = $val;
}
	foreach (sort {$a <=> $b} keys %order) {
	print OUT"$order{$_} - $hash{$order{$_}}";
}
 
the hashes are stored in an optimised fashion within Perl and not input order - this is quite normal


Kind Regards
Duncan
 
The point is I NEED it to be in insertion order. (first post)
 
maybe using an array of hashes would be the way to go?
 
I was simply pointing out that it isn't "strange" at all

I don't believe that it is possible to get them back in the input order without the module - but you may well receive a post that tells you how to do it... there's alot of clever dudes on this forum

Good luck :)


Kind Regards
Duncan
 
you might try to push the key of the hash onto a separate array, and reference that array when you pull from the hash ;-)


Your array would be pushed like such:

@array= key1, key2, key3;

while your hash looks like:
%hash={
key2=>value2
key1=>value1
key3=>value3};

then you can pull them out:

foreach $avalue (@array){

#the key being referenced
$hash{$avalue}

}

hope this helps!




___________________________________
[morse]--... ...--[/morse], Eric.
 
I think rharsh and nawlej have the right idea.
Code:
#!perl
use strict;
use warnings;

my (@order, %hash);
my ($key, $val);

while (<DATA>) {
    chomp;
    ($key, $val) = split /,/;
    push @order, $key;
    $hash{$key} = $val;
}

for (@order) {
    print qq($_ => $hash{$_}\n);
}

__DATA__
apples,red
grapes,purple
lemons,yellow
peaches,pink
kiwis,green
oranges,orange

Output:
apples => red
grapes => purple
lemons => yellow
peaches => pink
kiwis => green
oranges => orange
 
PerlGerl, in you're code, the %order hash needs to be populated at the same time you're populating %data, which means you can only really do it one key/value pair at a time.

Here's your code slightly modified.

Code:
my $text = "key1,val1,key2,val2,key3,val3,key4,val4";
my (%order, %data, $counter);
my @data = split (/,/, $text);
#my %data = ( @data ); 
#while( my($key, $val) = each(%data) ) {
while (@data) {
    my ($key, $val) = splice @data, 0, 2;
    $order{++$counter} = $key;
    $data{$key} = $val;
}
mikevh's code is probably the easiest way to go, using an array to track the order makes sense - it's all ready in order.
 
Thanks so much for all your help. rharsh - tried your method and it worked wonderfully. :)
 
I have this problem time and time again. I don't go through the trouble of creating arrays for this though.

The simplest (and surest method) that I've come up with is incrementing the hash keys as numbers. Then compare a and b to print/sort in insertion order.

Code:
$hash{"1"} = "oldhashkey1::value";
$hash{"2"} = "oldhashkey2::value";

Then split on the value to return the original hash key to return to having a key/value pair.

This is how I've always done it.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top