Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

O wise monks.... I have a hashof arrays into which I am adding data from <STDIN> I have input which looks like
<S> ::= <SS> <W> ::= <V> <V> ::= <SS>
To the left of the ::= are my keys and to the right the element to be stored in the array for each key. When I print out my HOA I get
<V> ::= <SS> <W> ::= <V> <S> ::= <SS>
Basically it storing the keys and array as a stack where its stored first in goes at the bottom and then each new set of key and corresponding array is stored above that. My question is how can I get it to store into the hash in the order of the input...In other words I want the keys and correspoinding array stored in the exact way the input is given. Here is the code I am using...I would really appreciate any help....thanks alot.
while($line=<>) { chomp $line; ($head, $prod) = split /\s*::=\s*/, $line; $head=~ s/^(\s+)//; $head=~ s/\s+$//; $prod=~ s/^(\s+)//; $prod=~ s/\s+$//; if($bool==0) { $start=$head; $bool=1; } push(@{$hasharr{$head}}, $prod); }
I apologize but it doesnt seem like im using the tags right

Replies are listed 'Best First'.
Re: hash property
by pg (Canon) on Oct 15, 2003 at 04:43 UTC

    The standard hash does not maintain any order, if you want some order being stored, for example the order pairs being added, then you have to do this your self (or use some other modules).

    If you prefer existing modules, check out Tie::IxHash.

    If you want to do it yourself, here is some OO solution I had, which I called it OrderedHash. The code given below is strictly for the purpose to explain the structure more clearly, not any kind of actual implementation.

    This OrderedHash structure contains two parts:
    1. An array, normal array, which contains the key pair values of the OrderedHash. In the Array, those key-value pairs are stored in this sequence: k1, v1, k2, v3, k3, v3...
    2. A hash, normal hash, its keys are the keys of the OrderedHash, values are indexes point to the key of the actual key value pairs in the above array.
    A brief analysis of this structure:
    1. It reserves the order.
    2. It is independent, yet not much implementing effort
    3. We don't invent or implement any hashing algorithm, we simply use a normal hash for indexing.
    4. Its performance is the same as a normal hash. We don't sacrifice performance.
    5. This solution DO sacrifice space to gain performance and to reduce implementation effort, it uses as twice as many space used by a normal hash that has the same number of elements.
    The following code is only to demo the structure.
    package OrderedHash; use strict; sub new { my $self = {}; $self->{INDEX} = {}; $self->{PAIRS} = []; bless $self; return $self; } sub set { my ($self, $key, $value) = @_; push @{$self->{PAIRS}}, $key; $self->{INDEX}->{$key} = $#{$self->{PAIRS}}; push @{$self->{PAIRS}}, $value; } sub get { my ($self, $key) = @_; return $self->{PAIRS}->[$self->{INDEX}->{$key} + 1]; } sub list_in_order { my $self = shift; my $is_key = 1; foreach (@{$self->{PAIRS}}) { if (!$is_key) { print "$_\n"; } else { print "[$_] = "; } $is_key = !$is_key; } } 1; use OrderedHash; use Data::Dumper; use strict; my $oh = new OrderedHash; $oh->set("a", 1); $oh->set("b", 2); $oh->set("c", 3); print Dumper($oh); $oh->list_in_order;
Re: hash property
by davido (Cardinal) on Oct 15, 2003 at 05:51 UTC
    As pg noted, a hash has no order (or actually, what may seem at times like random order, though few things with computers are purely random unless you're talking about mouse-click-sequences required to crash Windows).

    If you really need your hash to be sorted in a particular way, you can do that with a module like Tie::IxHash. Be forewarned that you use such approaches at the cost of considerable performance degredation.

    Another solution is to keep track yourself of the hash keys as they are created. Take the following example:

    use strict; use warnings; my (%hasharr, @hashkeys, $start); while ( my $line = <> ) chomp $line; next unless $line; #skip empty lines. my ($head, $prod) = split /\s*:=\s*/, $line; foreach ($head, $prod) { s/^\s+//; s/\s+$//; } unless ( $start ) { $start = $head; }; push ( @{$hasharr{$head}}, $prod); push @hashkeys, $head; }

    Yes, you're using a little more memory than before, because now you're maintaining a separate array of keys, in insertion order. But let me say that again, you have an array containing a list of keys in insertion order! Bingo. And as long as you're diligent about keeping the array and the hash in synch with each other, life is good. This approach won't scale as well as the Tie::IxHash method, but it is faster.

    Also note, my code snippet provides you with a few altered idioms for you to consider. They may not be exactly what you need, but they are "yet another way to do it". Especially regarding my method of eliminating the "$bool" variable, understand what's going on before copying my example, because it is functionally different if my implicit assertion (that $start is non-true, or empty, while $bool is non-true) is invalid.

    One more thing. Notice my use strict; and use warnings;. They are "A Very Good Idea" almost always (I'll stop short of "always"). And an even better idea for code that uses complex data structures (like yours), as they'll prevent you from accidentally using symbolic references, among other common pitfalls.


    Dave


    "If I had my life to do over again, I'd be a plumber." -- Albert Einstein
      thanks gentleman for all your help...i realized that i could solve my problem without ordering the hash in a certain way. However, your answers provided me with much insight.
Re: hash property
by Roger (Parson) on Oct 15, 2003 at 04:48 UTC
    Please use <code> and </code> to quote your code next time.