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

I have an HoHoH and I need to get information from it in the order it was inserted. As you can see, I have been messing around with Tie::IxHash but I can't get it to work for an HoHoH. Here is some demonstration code:
use Tie::IxHash; #tie %HoHoH, 'Tie::IxHash'; tie %{$HoHoH{text}}, 'Tie::IxHash'; my $HoHoH = {}; $HoHoH->{"alice"}{"aaaa"}{"time"} = "20:00:00"; $HoHoH->{"john"}{"ccc"}{"time"} = "21:00:00"; $HoHoH->{"alice"}{"www"}{"time"} = "22:00:00"; $HoHoH->{"john"}{"aaa"}{"time"} = "23:00:00"; $HoHoH->{"andy"}{"xxx"}{"time"} = "24:00:00"; $HoHoH->{"alice"}{"bbb"}{"time"} = "25:00:00"; $HoHoH->{"andy"}{"yyy"}{"time"} = "26:00:00"; foreach my $user ( sort keys %$HoHoH ) { print "$user\n"; for my $text ( keys %{$HoHoH->{ $user }} ) { print "\t$text"; for my $time ( keys %{$HoHoH->{ $user }->{ $text }} ) { print "\t$time = $HoHoH->{ $user }->{ $text }->{ $time }\n"; } } print "\n"; }
Here is what that code gives:
alice bbb time = 25:00:00 www time = 22:00:00 aaaa time = 20:00:00 andy yyy time = 26:00:00 xxx time = 24:00:00 john ccc time = 21:00:00 aaa time = 23:00:00
I am looking to sort according to names, but then I would like everything else to be output in the order it was inserted. In other words, output for alice should be:
aaaa time = 20:00:00<br> www time = 22:00:00<br> bbb time = 25:00:00<br>
It won't work to just sort by the time field as sometimes there will be more than one entry with the same time and it is imperative that they be in insertion order.

Much thanks for any ideas.

Replies are listed 'Best First'.
Re: HoHoH Insertion Order
by sacked (Hermit) on May 12, 2004 at 13:50 UTC
    You need to tie each inner hash (those keyed by "alice", "andy", and "john"):
    use Tie::IxHash; use strict; my $HoHoH = {}; tie %{$HoHoH->{$_}}, 'Tie::IxHash' foreach qw(alice john andy); + # rest of code as above # output: alice aaaa time = 20:00:00 www time = 22:00:00 bbb time = 25:00:00 andy xxx time = 24:00:00 yyy time = 26:00:00 john ccc time = 21:00:00 aaa time = 23:00:00
    Also, I suggest that you use strict;, as your original code had the following (lines three and four) which refer to different variables:
    # global %HoHoH tie %{$HoHoH{text}}, 'Tie::IxHash'; # lexical $HoHoH (reference) my $HoHoH = {};
    strict will catch this at compile time:
    Global symbol "%HoHoH" requires explicit package name at /tmp/1.pl lin +e 4.
    Your code ties a different variable than you were using everywhere else in your code. In addition, the key "text" that points to the inner hash stored in your global hash %HoHoH isn't used anywhere else.

    Hope this helps.

    --sacked
Re: HoHoH Insertion Order
by jeffa (Bishop) on May 12, 2004 at 13:52 UTC

    Your datastructure is not an ideal fit. Instead, try a Hash of Arrays of Hashes:

    use strict; use warnings; use Data::Dumper; my %hash; push @{$hash{alice}}, {aaaa => "20:00:00"}; push @{$hash{john}}, {ccc => "21:00:00"}; push @{$hash{alice}}, {www => "22:00:00"}; push @{$hash{john}}, {aaa => "23:00:00"}; push @{$hash{andy}}, {xxx => "24:00:00"}; push @{$hash{alice}}, {bbb => "25:00:00"}; push @{$hash{andy}}, {yyy => "26:00:00"}; print Dumper $hash{alice}; __END__ $VAR1 = [ { 'aaaa' => '20:00:00' }, { 'www' => '22:00:00' }, { 'bbb' => '25:00:00' } ];
    Because each { foo => 'time' } anonymous hash is stored in a list, the order is preserved and Tie::IxHash is not needed.

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: HoHoH Insertion Order
by pelagic (Priest) on May 12, 2004 at 13:47 UTC
    Why are you actually using a HoHoH?
    Is there more information besides the time?
    If not you could say:
    $HoHoH->{"alice"}{"aaaa"} = "20:00:00"; # instead of # $HoHoH->{"alice"}{"aaaa"}{"time"} = "20:00:00";
    and later:
    foreach my $user ( sort keys %$HoHoH ) { print "$user\n"; for my $text ( sort {$HoHoH->{$user}->{$a} cmp $HoHoH->{$user}->{$b} } keys %{$HoHoH->{ $user }} ) { ...

    pelagic
Re: HoHoH Insertion Order
by Abigail-II (Bishop) on May 12, 2004 at 13:53 UTC
    You need to create tied hashes on each level. For instance:
    #!/usr/bin/perl use strict; use warnings; use Tie::IxHash; my @entries = ([qw /alice aaa time 20:00:00/], [qw /john ccc time 21:00:00/], [qw /alice www time 22:00:00/], [qw /john aaa time 23:00:00/], [qw /andy xxx time 24:00:00/], [qw /alice bbb time 25:00:00/], [qw /andy yyy time 26:00:00/], ); my %HoHoH; tie %HoHoH => 'Tie::IxHash'; foreach my $entry (@entries) { my ($key1, $key2, $key3, $value) = @$entry; tie %{$HoHoH {$key1}} => 'Tie::IxHash' unless exists $HoHoH {$key1 +}; tie %{$HoHoH {$key1} {$key2}} => 'Tie::IxHash' unless exists $HoHoH {$key1 +} {$key2}; $HoHoH {$key1} {$key2} {$key3} = $value; } while (my ($key1, $val1) = each %HoHoH) { while (my ($key2, $val2) = each %$val1) { while (my ($key3, $val3) = each %$val2) { print "$key1 $key2 $key3 -> $val3\n"; } } } __END__ alice aaa time -> 20:00:00 alice www time -> 22:00:00 alice bbb time -> 25:00:00 john ccc time -> 21:00:00 john aaa time -> 23:00:00 andy xxx time -> 24:00:00 andy yyy time -> 26:00:00

    Abigail

Re: HoHoH Insertion Order
by japhy (Canon) on May 12, 2004 at 15:43 UTC
    I did it for MLDBM (here: MLDBM::Easy), so I'll do it for Tie::IxHash. This simple module will be on CPAN in an hour or two as Tie::IxHash::Easy (if you have a better name, /msg me or reply here).
    package Tie::IxHash::Easy; use Tie::IxHash; @ISA = 'Tie::IxHash'; sub STORE { my ($self, $key, $value) = @_; tie %$value, 'Tie::IxHash::Easy' if ref($value) eq "HASH"; $self->SUPER::STORE($key, $value); } 1;
    The module will have strictures and warnings for the fun of it, and documentation, and a version number, of course.

    To use it, just use Tie::IxHash::Easy instead of Tie::IxHash in your code. You only need to tie the outermost hash. Hashes created inside the tied hash will be auto-tied by Tie::IxHash::Easy::STORE.

    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;
Re: HoHoH Insertion Order
by CombatSquirrel (Hermit) on May 12, 2004 at 13:53 UTC
    I think you need to tie every sub-hash as well, like this:
    #!perl use strict; use warnings; use Tie::IxHash; my $HoHoH = {}; tie %$HoHoH, 'Tie::IxHash'; tie %{$HoHoH->{'alice'}}, 'Tie::IxHash'; tie %{$HoHoH->{'john'}}, 'Tie::IxHash'; tie %{$HoHoH->{'andy'}}, 'Tie::IxHash'; $HoHoH->{"alice"}{"aaaa"}{"time"} = "20:00:00"; $HoHoH->{"john"}{"ccc"}{"time"} = "21:00:00"; $HoHoH->{"alice"}{"www"}{"time"} = "22:00:00"; $HoHoH->{"alice"}{"xxx"}{"time"} = "22:00:00"; $HoHoH->{"john"}{"aaa"}{"time"} = "23:00:00"; $HoHoH->{"andy"}{"xxx"}{"time"} = "24:00:00"; $HoHoH->{"alice"}{"bbb"}{"time"} = "25:00:00"; $HoHoH->{"andy"}{"yyy"}{"time"} = "26:00:00"; foreach my $user (sort keys %$HoHoH) { printf "%s\n", $user; for my $text (keys %{$HoHoH->{$user}}) { printf "%s%-5.5s", ' ' x 3, $text; for my $time (keys %{$HoHoH->{$user}->{$text}}) { printf "%s%s = %s\n", ' ' x 6, $time, $HoHoH->{$user}->{$text}-> +{$time}; } } print "\n"; }
    At least it works (for me ;-).
    Hope this helped.
    CombatSquirrel.
    Entropy is the tendency of everything going to hell.