Trouble converting array to hash - arrays

I have an array where elements of the array have values that are separated by tabs.
For example:
client_name \t owner \t date \t port_number.
I need to convert that into a hash so it can be dumped into a MySQL database.
Something like:
my %foo = ();
$foo{date} = "111208";
$foo{port} = "2222";
$foo{owner} = "ownername";
$foo{name} = "clientname";
The problem I have is that there are duplicate client names but they exist on different port numbers. If I convert it directly to a hash using client_name as a key it will delete duplicate client names. The MySQL table is indexed based on {name} and {port}.
Is there any way I can convert this into a hash without losing duplicate client names?

You would go through your file, build up the hash like you've done, then push a reference to that hash onto an array. Something like:
foreach my $line ( #lines ) {
# Make your %foo hash.
push #clients, \%foo;
}
Then afterwards, when you're inserting into your DB, you just iterate through the elements in #clients:
foreach my $client ( #clients ) {
$date = $client->{'date'};
...
}
Edit: If you want to turn this into a hash of hashes, then as you loop through the list of lines, you'd do something like:
foreach my $line ( #lines ) {
# Make your %foo hash.
$clients{$foo{'port'}} = \%foo;
}
Then you'll have a hash of hashes using the port number as the key.

Why not just store it in a list (array)?
my #records = ();
while (my $line = <INFILE>) {
chomp $line;
my #fields = split /\t/ $line;
push #records => { date => $fields[2],
name => $fields[0],
port => $fields[3],
owner => $fields[1] };
}
for my $record (#records) {
$insert_query->execute (%$record);
}

my #record_list;
while ( <$generic_input> ) {
my $foo = {};
#$foo{ qw<date port owner name> } = split /\t/;
push #record_list, \%foo;
}
As a "pipeline" you could do this:
use List::MoreUtils qw<pairwise>;
my #fields = qw<date port owner name>;
my #records
= map {; { pairwise { $a => $b } #fields, #{[ split /\t/ ]}}}
<$input>
;

Related

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";
}

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
}
}

Auto increment numeric key values in a perl hash?

I have a perl script in which I am reading files from a given directory, and then placing those files into an array. I then want to be able to move those array elements into a perl hash, with the array elements being the hash value, and automatically assigning numeric keys to each hash value.
Here's the code:
# Open the current users directory and get all the builds. If you can open the dir
# then die.
opendir(D, "$userBuildLocation") || die "Can't opedir $userBuildLocation: $!\n";
# Put build files into an array.
my #builds = readdir(D);
closedir(D);
print join("\n", #builds, "\n");
This print out:
test.dlp
test1.dlp
I want to take those value and insert them into a hash that looks just like this:
my %hash (
1 => test.dlp
2 => test1.dlp
);
I want the numbered keys to be auto incrementing based on how many files I may find in a given directory.
I'm just not sure how to get the auto-incrementing keys to be set to unique numeric values for each item in the hash.
I am not sure to understand the need, but this should do
my $i = 0;
my %hash = map { ++$i => $_ } #builds;
another way to do it
my $i = 0;
for( #builds ) {
$hash{++$i} = $_;
}
The most straightforward and boring way:
my %hash;
for (my $i=0; $i<#builds; ++$i) {
$hash{$i+1} = $builds[$i];
}
or if you prefer:
foreach my $i (0 .. $#builds) {
$hash{$i+1} = $builds[$i];
}
I like this approach:
#hash{1..#builds} = #builds;
Another:
my %hash = map { $_+1, $builds[$_] } 0..$#builds;
or:
my %hash = map { $_, $builds[$_-1] } 1..#builds;

Perl: Group input data by first field

I have a data file that contains an interface name and destination. I want to group all destinations by interface so that I can iterate through and store results. Here is an example of my output:
eth0,1.1.1.1
eth0,1.1.1.2
eth1,1.1.1.1
eth1,1.1.1.2
How do i dump the unique interface values into a hash and build an array of destinations?
my %ifs;
while ( my $line = <STDIN> ) {
chomp $line;
my ( $iface, $destination ) = split /,/, $line;
push #{ $ifs{ $iface } }, $destination;
}
Should work.

Perl Array of Hashes - Reference each hash within array?

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;

Resources