Perl Array of Hashes - Reference each hash within array? - arrays

I am trying to create an array of hashes, and I am wondering how to reference each hash within the array?
For eg:
while(<INFILE>)
{
my $row = $_;
chomp $row;
my #cols = split(/\t/,$row);
my $key = $cols[0]."\t".$cols[1];
my #total = (); ## This is my array of hashes - wrong syntax???
for($i=2;$i<#cols;$i++)
{
$total[$c++]{$key} += $cols[$i];
}
}
close INFILE;
foreach (sort keys %total) #sort keys for one of the hashes within the array - wrong syntax???
{
print $_."\t".$total[0]{$_}."\n";
}
Thanks in advance for any help.

You don't need
my #total = ();
This:
my #total;
is sufficient for what you are after. No special syntax needed to declare that your array will contain hashes.
There's probably different ways of doing the foreach part, but this should work:
foreach (sort keys %{$total[$the_index_you_want]}) {
print $_."\t".$total[$the_index_you_want]{$_}."\n";
}
[BTW, declaring my #total; inside that loop is probably not what you want (it would be reset on each line). Move that outside the first loop.]
And use strict; use warnings;, obviously :-)

Here's what I get:
print join( "\t", #$_{ sort keys %$_ } ), "\n" foreach #total;
I'm simply iterating through #total and for each hashref taking a slice in sorted order and joining those values with tabs.
If you didn't need them in sorted order, you could just
print join( "\t", values %$_ ), "\n" foreach #total;
But I also would compress the line processing like so:
my ( $k1, $k2, #cols ) = split /\t/, $row;
my $key = "$k1\t$k2";
$totals[ $_ ]{ $key } += $cols[ $_ ] foreach 0..$#cols;
But with List::MoreUtils, you could also do this:
use List::MoreUtils qw<pairwise>;
my ( $k1, $k2, #cols ) = split /\t/, $row;
my $key = "$k1\t$k2";
pairwise { $a->{ $key } += $b } #totals => #cols;

Related

Perl array - trying to parse quotes in proper array elements

I have been struggling with this for a while in a Perl script I have. Probably a slam dunk for you Perl experts, and probably should be easier, but I can't quite crack the nut on this. I might be needing to split this, not sure.
My array code as is follows.
while ( my $row = $query_handle->fetchrow_hashref('NAME_lc') ){
push #query_output, $row;
push (#{portfo->{label}},$row->{data},$row->{label});
}
And then my print of the array is as follows:
print "array here--";
print "[";
foreach (#{portfo->{label}}) {
#(#{portfo->{label}},$row->{data});
print "\{\"data\":";
print "".$_.",";
print "\"label\":";
print "\"".$row[1]."\"\},";
}
print "]";
print "\n";
And then my output looks like this:
[{"data":2943,"label":""},{"data":CDI3,"label":""},
{"data":1,"label":""},{"data":COS-COS2,"label":""},
{"data":1087,"label":""},{"data":COS1,"label":""},
{"data":5183,"label":""},{"data":COS2,"label":""},
{"data":2731,"label":""},{"data":CULB,"label":""},{"data":1,"label":""},
{"data":EQUIT,"label":""},{"data":4474,"label":""},
{"data":Network,"label":""},]
I am trying to make the apha-num string array items like CDI3, COS1, COS2, etc in quotes, in the label part. Somehow I'm getting it separated. Meanwhile, I do want the numeric values left with the "data" name pair.
[{"data":2943,"label":""},{"data":"CDI3","label":""},
{"data":1,"label":""},{"data":"COS-COS2","label":""},
{"data":1087,"label":""},{"data":"COS1","label":""},
{"data":5183,"label":""},{"data":"COS2","label":""},
{"data":2731,"label":""},{"data":"CULB","label":""},{"data":1,"label":""},
{"data":"EQUIT","label":""},{"data":4474,"label":""},
{"data":"Network","label":""}]
I'm sure it's a simpler fix that I'm making it but so far no luck. Any tips would be greatly appreciated!
Thanks!
use JSON::XS qw( encode_json );
my #data;
while ( my $row = $query_handle->fetchrow_hashref('NAME_lc') ) {
# If $row->{data} is a number,
# make sure it's stored as a number
# so that it gets serialized as a number.
$row->{data} += 0 if $row->{data} =~ /^\d+\z/;
push #data, $row;
}
print(encode_json(\#data));
Or
my $data = $query_handle->fetchall_arrayref({ data => 1, label => 1 });
for my $row (#$data) {
$row->{data} += 0 if $row->{data} =~ /^\d+\z/;
}
print(encode_json($data));
Or if you ensure the fields names are returned as lowercase[1],
my $data = $query_handle->fetchall_arrayref({});
for my $row (#$data) {
$row->{data} += 0 if $row->{data} =~ /^\d+\z/;
}
print(encode_json($data));
This can be done using $dbh->{FetchHashKeyName} = 'NAME_lc'; or AS `label`.

Separating CSV file into key and array

I am new to perl, and I am trying to separate a csv file (has 10 comma-separated items per line) into a key (first item) and an array (9 items) to put in a hash. Eventually, I want to use an if function to match another variable to the key in the hash and print out the elements in the array.
Here's the code I have, which doesn't work right.
use strict;
use warnings;
my %hash;
my $in2 = "metadata1.csv";
open IN2, "<$in2" or die "Cannot open the file: $!";
while (my $line = <IN2>) {
my ($key, #value) = split (/,/, $line, 2);
%hash = (
$key => #value
);
}
foreach my $key (keys %hash)
{
print "The key is $key and the array is $hash{$key}\n";
}
Thank you for any help!
Don't use 2 as the third argument to split: it will split the line to only two elements, so there'll be just one #value.
Also, by doing %hash =, you're overwriting the hash in each iteration of the loop. Just add a new key/value pair:
$hash{$key} = \#value;
Note the \ sign: you can't store an array directly as a hash value, you have to store a reference to it. When printing the value, you have to dereference it back:
#! /usr/bin/perl
use warnings;
use strict;
my %hash;
while (<DATA>) {
my ($key, #values) = split /,/;
$hash{$key} = \#values;
}
for my $key (keys %hash) {
print "$key => #{ $hash{$key} }";
}
__DATA__
id0,1,2,a
id1,3,4,b
id2,5,6,c
If your CSV file contains quoted or escaped commas, you should use Text::CSV.
First of all hash can have only one unique key, so when you have lines like these in your CSV file:
key1,val11,val12,val13,val14,val15,val16,val17,val18,val19
key1,val21,val22,val23,val24,val25,val26,val27,val28,val29
after adding both key/value pairs with 'key1' key to the hash, you'll get just one pair saved in the hash, the one that were added to the hash later.
So to keep all records, the result you probably need array of hashes structure, where value of each hash is an array reference, like this:
#result = (
{ 'key1' => ['val11','val12','val13','val14','val15','val16','val17','val18','val19'] },
{ 'key1' => ['val21','val22','val23','val24','val25','val26','val27','val28','val29'] },
{ 'and' => ['so on'] },
);
In order to achieve that your code should become like this:
use strict;
use warnings;
my #AoH; # array of hashes containing data from CSV
my $in2 = "metadata1.csv";
open IN2, "<$in2" or die "Cannot open the file: $!";
while (my $line = <IN2>) {
my #string_bits = split (/,/, $line);
my $key = $string_bits[0]; # first element - key
my $value = [ #string_bits[1 .. $#string_bits] ]; # rest get into arr ref
push #AoH, {$key => $value}; # array of hashes structure
}
foreach my $hash_ref (#AoH)
{
my $key = (keys %$hash_ref)[0]; # get hash key
my $value = join ', ', #{ $hash_ref->{$key} }; # join array into str
print "The key is '$key' and the array is '$value'\n";
}

ID tracking while swapping and sorting other two arrays in perl

#! /usr/bin/perl
use strict;
my (#data,$data,#data1,#diff,$diff,$tempS,$tempE, #ID,#Seq,#Start,#End, #data2);
#my $file=<>;
open(FILE, "< ./out.txt");
while (<FILE>){
chomp $_;
#next if ($line =~/Measurement count:/ or $line =~/^\s+/) ;
#push #data, [split ("\t", $line)] ;
my #data = split('\t');
push(#ID, $data[0]);
push(#Seq, $data[1]);
push(#Start, $data[2]);
push(#End, $data[3]);
# push #$data, [split ("\t", $line)] ;
}
close(FILE);
my %hash = map { my $key = "$ID[$_]"; $key => [ $Start[$_], $End[$_] ] } (0..$#ID);
for my $key ( %hash ) {
print "Key: $key contains: ";
for my $value ($hash{$key} ) {
print " $hash{$key}[0] ";
}
print "\n";
}
for (my $j=0; $j <=$#Start ; $j++)
{
if ($Start[$j] > $End[$j])
{
$tempS=$Start[$j];
$Start[$j]=$End[$j];
$End[$j]=$tempS;
}
print"$tempS\t$Start[$j]\t$End[$j]\n";
}
my #sortStart = sort { $a <=> $b } #Start;
my #sortEnd = sort { $a <=> $b } #End;
#open(OUT,">>./trial.txt");
for(my $i=1521;$i>=0;$i--)
{
print "hey";
my $diff = $sortStart[$i] - $sortStart[$i-1];
print "$ID[$i]\t$diff\n";
}
I have three arrays of same length, ID with IDs (string), Start and End with integer values (reading from a file).
I want to loop through all these arrays and also want to keep track of IDs. First am swapping elements in Start with End if Start > End, then I have to sort these two arrays for further application (as I am negating Start[0]-Start[1] for each item in that Start). While sorting, the Id values may change, and as my IDs are unique for each Start and End elements, how can I keep track of my IDs while sorting them?
Three arrays, ID, Start and End, are under my consideration.
Here is a small chunk of my input data:
DQ704383 191990066 191990037
DQ698580 191911184 191911214
DQ724878 191905507 191905532
DQ715191 191822657 191822686
DQ722467 191653368 191653339
DQ707634 191622552 191622581
DQ715636 191539187 191539157
DQ692360 191388765 191388796
DQ722377 191083572 191083599
DQ697520 189463214 189463185
DQ709562 187245165 187245192
DQ540163 182491372 182491400
DQ720940 180753033 180753060
DQ707760 178340696 178340726
DQ725442 178286164 178286134
DQ711885 178250090 178250119
DQ718075 171329314 171329344
DQ705091 171062479 171062503
The above ID, Start, End respectively. If Start > End i swapped them only between those two arrays. But after swapping the descending order may change, but i want them in descending order also their corresponding ID for negation as explained above.
Don't use different arrays, use a hash to keep the related pieces of information together.
#!/usr/bin/perl
use warnings;
use strict;
use enum qw( START END );
my %hash;
while (<>) {
my ($id, $start, $end) = split;
$hash{$id} = [ $start < $end ? ($start, $end)
: ($end, $start) ];
}
my #by_start = sort { $hash{$a}[START] <=> $hash{$b}[START] } keys %hash;
my #by_end = sort { $hash{$a}[END] <=> $hash{$b}[END] } keys %hash;
use Test::More;
is_deeply(\#by_start, \#by_end, 'same');
done_testing();
Moreover, in the data sample you provided, the order of id's is the same regardless of by what you sort them.

What is the equivalent hash function of "push' arrays in perl?

I am a beginner programmer who is writing a program using perl that will eventually allow me to search for name, and have it tell me the early steps. So far (with the help of many nice people on here) I have this code for array format.
#!/usr/local/bin/perl
use strict;
use warnings;
my #M_array;
my #F_array;
open (my $input, "<", 'ssbn1898.txt');
while ( <$input> ) {
chomp;
my ( $name, $id ) = split ( /,/ );
if ( $id eq "M" ) {
push ( #M_array, $name );
}
else {
push ( #F_array, $name );
}
}
close ( $input );
print 'M: ' . join("\t", #M_array) . "\n";
print 'F: ' . join("\t", #F_array) . "\n";
And I attempted to use the same code to put it into a hash.
#!/usr/local/bin/perl
use strict;
use warnings;
my %M_hash;
my %F_hash;
open (my $input, "<", 'ssbn1898.txt');
while ( <$input> ) {
chomp;
my ( $name, $id ) = split ( /,/ );
if ( $id eq "M" ) {
push ( %M_hash, $name );
}
else {
push ( %F_hash, $name );
}
}
close ( $input );
print 'M: ' . join("\t", %M_hash) . "\n";
print 'F: ' . join("\t", %F_hash) . "\n";
But I get an error on the "push" function. I would assume then that this function is just for arrays. Is there an equivalent function for a hash? And what does the "push" function really do?
Thank you all for your help
http://www.ourbabynamer.com/popular-baby-names.php?year=1898&top=1000&country=US&order=0&page=1
This is the data I am working with
Push adds an element to the back of an array.
#a = ( 1, 2, 3 );
push #a, 4; # #a is now ( 1, 2, 3, 4 )
Insert adds an element to a hash.
%h = ( foo => 1, bar => 2 );
$h{ qux } = 3; # %h is now ( foo => 1, bar => 2, qux => 3 );
Take a look at perldoc perlintro
http://perldoc.perl.org/perlintro.html
push adds an element at the end of an array. Hashes don't have an end, so there's no equivalent for hashes. You need to specify the key of the element you wish to set.
$hash{$key} = $val;
I don't know why you changed the array into a hash. It makes no sense to use a hash here. The solution is to revert your change.
In a comment, you say that you "must use this data as an array and a hash". I'm not really sure what you mean, but one possible interpretation is that your teacher wants you do use both hashes and arrays in your code.
One way to do that would be to store your data in an hash of arrays. It would look something like this.
#!/usr/local/bin/perl
use strict;
use warnings;
use 5.010;
my %data;
while ( <> ) { # Use <> to read from STDIN. Makes life easier :-)
chomp;
my ( $name, $gender ) = split /,/;
push #{$data{$gender}}, $name;
}
foreach (keys %data) {
say "$_: " . join("\t", #{$data{$_}_);
}
But that would involve using array references, which sounds like it might be a little advanced for your current course.
One advantage of this approach is that it will continue to work (without code changes) should you wish to add new genders to your input data!
push works on arrays, read more at: http://perldoc.perl.org/functions/push.html
Hashes are different than arrays, they are like associate arrays. They are un-ordered group of key-value pairs. To add some key to hash you do something like below:
my %hash = (key1 => "value1", key2 => "value2");
$hash{"key3"} = "value3";
Note that keys must be unique.
See also:
Hashes in Perl - Gabor's blog
keys - perldoc
values - perldoc
There is no specific function to push the element to the hash, you just need assign the value to hash key as below -
$hash{key} = 'value';
Though it will not sure that this item will be appended as the last element as the hash stores it keys in random fashion,

Looping through array that's inside hash - perl

Am I doing this right?
my $variable = "hello,world";
my #somearray = split(',',$variable);
my %hash = ( 'somevalue' => #somearray);
foreach my $key ( keys %hash ) {
print $key;
foreach my $value ( #{$hash{$key}} ) {
print $value; #the value is not being read/printed
}
}
I don't know if I'm accessing the array that is stored in the hash for the particular value
You've been bitten by perl's flattening nature of lists. You see, when you do:
my %hash = ('somekey' => #somearray), perl converts that into a list form of hash assignment. So, what perl actually sees is:
my %hash = ('somekey' => 'hello', 'world' => ''); # I have put '' for simplicity, though it might well be `undef`
So, the next time you look up by 'somekey', you end up getting the string 'hello' and not the array "['hello', 'world']"
To fix this, you can use references. perlref can help you there for more information.
my %hash = ('somekey' => \#somearray);
# $hash{'somekey'} is an array reference now.
# So you use the pointy lookup syntax.
print $hash{'somekey'}->[0];
Another useful tool in visualising data structures is using the module Data::Dumper. It's available in the perl core distribution. Using it is as simple as doing:
use Data::Dumper;
print Dumper \%hash; # remember to pass in a reference to the actual datastructure, not the data structure itself.
Have fun!
This is wrong:
my %hash = ( 'somevalue' => #somearray);
The array is "flattened" to a list, so the line is equivalent to
my %hash = qw( somevalue hello world );
You need an array reference to create the inner array:
my %hash = ( 'somevalue' => \#somearray);
So you wanted to create a hash of array data structure. Something like this will also work.
my $variable = "hello,world";
my #somearray = split(',',$variable);
my %hash;
#my %hash = ( 'somevalue' => #somearray);
push (#{$hash{'somevalue'}},#somearray); #Storing it in a hash of array
foreach my $key ( keys %hash ) {
print $key;
foreach my $value ( #{$hash{$key}} ) {
print $value; #the value is not being read/printed
}
}

Resources