Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

storing values of hash of hashes in an array based on numerical order of second key

by v15 (Sexton)
on Aug 17, 2020 at 21:28 UTC ( [id://11120854]=perlquestion: print w/replies, xml ) Need Help??

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

I have a hash of hashes data structure. Name of the hash is %pos2base2bin and using data dumper, these are the contents of the hash:
$VAR1 = { '315' => { '8' => 0 }, '329' => { '6' => 0 }, '352' => { '5' => 0 }, '390' => { '1' => 0 }, '280' => { '7' => 1 }, '360' => { '9' => 0 }, '349' => { '4' => 0 }, '305' => { '10' => 0 }, '380' => { '3' => 1 }, '251' => { '2' => 0 } };
I want to sort the second key numerically($a <=> $b) and then store the values(0's and 1's) in an array in the numerical order of the second keys. The goal is to print this array with values tab separated. My output should be like this:
sample 1 2 3 4 5 6 7 8 9 10 file1 0 0 1 0 0 0 1 0 0 0
I already have the header line starting with sample. Just have to place the respective values beneath it. How can I achieve this? This is what works for me but I think there must be an easier way to do it:
my %output=(); foreach my $k1(keys %pos2base2bin){ foreach my $k2 (sort{$a <=> $b} keys %{ $pos2base2bin{$k1} }){ push @{$output{$k2}},$pos2base2bin{$k1}{$k2} } } my @array=(); foreach my $k(sort{$a <=> $b} keys %output){ foreach my $val(@{$output{$k}}){ push @array,$val; } } print join("\t",$f,@array),"\n"; # $f is the filename

Replies are listed 'Best First'.
Re: storing values of hash of hashes in an array based on numerical order of second key
by BillKSmith (Monsignor) on Aug 17, 2020 at 23:04 UTC
    This can be solved with a special case of the Schwartzian Transform. How do I sort an array by (anything)
    use strict; use warnings; my %pos2base2bin = ( '315' => { '8' => 0 }, '329' => { '6' => 0 }, '352' => { '5' => 0 }, '390' => { '1' => 0 }, '280' => { '7' => 1 }, '360' => { '9' => 0 }, '349' => { '4' => 0 }, '305' => { '10' => 0 }, '380' => { '3' => 1 }, '251' => { '2' => 0 } ); my @file1 = map {$_->[1]} sort {$a->[0] <=> $b->[0]} map {[each %$_]} values %pos2base2bin; $"= "\t"; print "file1 @file1\n";
    Bill
Re: storing values of hash of hashes in an array based on numerical order of second key
by tybalt89 (Monsignor) on Aug 18, 2020 at 03:32 UTC
    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11120854 use warnings; my %pos2base2bin = ( '315' => { '8' => 0 }, '329' => { '6' => 0 }, '352' => { '5' => 0 }, '390' => { '1' => 0 }, '280' => { '7' => 1 }, '360' => { '9' => 0 }, '349' => { '4' => 0 }, '305' => { '10' => 0 }, '380' => { '3' => 1 }, '251' => { '2' => 0 } ); local ($\, $,) = ("\n", "\t"); my @items = sort { $a->[0] <=> $b->[0] } map [%$_], values %pos2base2b +in; print 'sample', map $_->[0], @items; print 'file1', map $_->[1], @items;
Re: storing values of hash of hashes in an array based on numerical order of second key
by johngg (Canon) on Aug 18, 2020 at 14:30 UTC

    Discard the "pos" part of %pos2base2bin, sort the keys of %base2bin, print those on the first line and print a hash slice on the second.

    use 5.026; use warnings; my %pos2base2bin = ( '315' => { '8' => 0 }, '329' => { '6' => 0 }, '352' => { '5' => 0 }, '390' => { '1' => 0 }, '280' => { '7' => 1 }, '360' => { '9' => 0 }, '349' => { '4' => 0 }, '305' => { '10' => 0 }, '380' => { '3' => 1 }, '251' => { '2' => 0 } ); my %base2bin = map { %{ $_ } } values %pos2base2bin; my @order = sort { $a <=> $b } keys %base2bin; say qq{sample\t}, join qq{\t}, @order; say qq{file1\t}, join qq{\t}, @base2bin{ @order };

    Produces:-

    sample 1 2 3 4 5 6 7 8 + 9 10 file1 0 0 1 0 0 0 1 0 + 0 0

    I hope this is of interest.

    Cheers,

    JohnGG

Re: storing values of hash of hashes in an array based on numerical order of second key
by wazat (Monk) on Aug 18, 2020 at 00:04 UTC

    I like Bill's reply

    here is another way. I'm assuming there are no collisions among the 2nd level keys. I also assume that the 2nd level keys are non-negative integers and are not large numbers. I also note that you don't use the 1st level keys.

    use strict; use warnings; my $VAR1 = { '315' => {'8' => 0}, '329' => {'6' => 0}, '352' => {'5' => 0}, '390' => {'1' => 0}, '280' => {'7' => 1}, '360' => {'9' => 0}, '349' => {'4' => 0}, '305' => {'10' => 0}, '380' => {'3' => 1}, '251' => {'2' => 0} }; my @a; for my $x (values %$VAR1 ) { for my $y ( keys %$x ) { $a[$y] = [$y, $x->{$y}]; } } print join("\t", ( map { $_->[0]; } grep { defined } @a ) ), "\n"; print join("\t", ( map { $_->[1]; } grep { defined } @a ) ), "\n";
Re: storing values of hash of hashes in an array based on numerical order of second key
by LanX (Saint) on Aug 17, 2020 at 21:43 UTC
    Dive into the hash of hashes with two nested while each loops and populate a temporary array.

    Print the array's content afterwards.

    If the data is in reality more sparse, populate a hash and sort the keys before printing the table.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      each is basically evil. Don't use it.

      The iterator used by each is attached to the hash or array, and is shared between all iteration operations applied to the same hash or array. Thus all uses of each on a single hash or array advance the same iterator location. All uses of each are also subject to having the iterator reset by any use of keys or values on the same hash or array, or by the hash (but not array) being referenced in list context. This makes each-based loops quite fragile: it is easy to arrive at such a loop with the iterator already part way through the object, or to accidentally clobber the iterator state during execution of the loop body. It's easy enough to explicitly reset the iterator before starting a loop, but there is no way to insulate the iterator state used by a loop from the iterator state used by anything else that might execute during the loop body. To avoid these problems, use a foreach loop rather than while-each.
      
        I'm aware, but in this case there is no potential effect at a distance.

        We just loop without calling a sub routine and sharing the hash.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11120854]
Approved by Athanasius
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (5)
As of 2024-04-23 23:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found