Tie::IxHash ordered associative arrays in Hash of Hashes? - perl

How can I preserve the order in which the hash elements were added
FOR THE SECOND VAR ?
( Hash of Hashes )
For example:
use Tie::IxHash;
my %hash;
tie %hash, "Tie::IxHash";
for my $num (0 .. 5){
$hash{"FirstVal$num"}++;
}
for my $num (0 .. 5){
$hash{"FirstValFIXED"}{"SecondVal$num"}++;
}
print Dumper(%hash);
When dumping out the result, $VAR14 didn't preserve the insertion order:
$VAR1 = 'FirstVal0';
$VAR2 = 1;
$VAR3 = 'FirstVal1';
$VAR4 = 1;
$VAR5 = 'FirstVal2';
$VAR6 = 1;
$VAR7 = 'FirstVal3';
$VAR8 = 1;
$VAR9 = 'FirstVal4';
$VAR10 = 1;
$VAR11 = 'FirstVal5';
$VAR12 = 1;
$VAR13 = 'FirstValFIXED';
$VAR14 = {
'SecondVal5' => 1,
'SecondVal4' => 1,
'SecondVal2' => 1,
'SecondVal1' => 1,
'SecondVal3' => 1,
'SecondVal0' => 1
};
I know I can trick that example with some sort operation but in my real problem the elements are not numbered or can't be ordered some how.
Is there any simple function/operation for hash multi level order insertion ?
Thanks,
Yodar.

Look at Tie::Autotie. It automatically ties new hashes created by autovivification. The perldoc page shows an example using Tie::IxHash.

You need an extra "\", as below.
print Dumper(\%hash);

Do you mean hash of hashes? You need to tie to Tie::IxHash every value of outer hash.
use strict;
use warnings;
use Tie::IxHash;
my $hash={};
my $t = tie(%$hash, 'Tie::IxHash', 'a' => 1, 'b' => 2);
%$hash = (first => 1, second => 2, third => 3);
$hash->{fourth} = 4;
print join(', ',keys %$hash),"\n";
my %new_hash=('test'=>$hash);
$new_hash{'test'}{fifth} = 5;
print join(', ',keys %{$new_hash{'test'}}),"\n";
$new_hash{'test'}{fifth}++;
print join(', ',values %{$new_hash{'test'}}),"\n";

foreach my $sortline (sort {$a<=>$b} keys %{$hash->{"first_field"}}){
my $name;
# Soultion to touch a Key with keys within it:
#---------------------------------------------
foreach my $subkey (keys %{$hash->{"first_field"}->{$sortline}})
{$name = $subkey;}
#---------------------------------------------
}
This useful answer helped me.

Related

How to split a string into multiple hash keys in perl

I have a series of strings for example
my #strings;
$strings[1] = 'foo/bar/some/more';
$strings[2] = 'also/some/stuff';
$strings[3] = 'this/can/have/way/too/many/substrings';
What I would like to do is to split these strings and store them in a hash as keys like this
my %hash;
$hash{foo}{bar}{some}{more} = 1;
$hash{also}{some}{stuff} = 1;
$hash{this}{can}{have}{way}{too}{many}{substrings} = 1;
I could go on and list my failed attempts, but I don't think they add to the value to the question, but I will mention one. Lets say I converted 'foo/bar/some/more' to '{foo}{bar}{some}{more}'. Could I somehow store that in a variable and do something like the following?
my $var = '{foo}{bar}{some}{more}';
$hash$var = 1;
NOTE: THIS DOESN'T WORK, but I hope it only doesn't due to a syntax error.
All help appreciated.
Identical logic to Shawn's answer. But I've hidden the clever hash-walking bit in a subroutine. And I've set the final value to 1 rather than an empty hash reference.
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use Data::Dumper;
my #keys = qw(
foo/bar/some/more
also/some/stuff
this/can/have/way/too/many/substrings
);
my %hash;
for (#keys) {
multilevel(\%hash, $_);
}
say Dumper \%hash;
sub multilevel {
my ($hashref, $string) = #_;
my $curr_ref = $hashref;
my #strings = split m[/], $string;
for (#strings[0 .. $#strings - 1]) {
$curr_ref->{$_} //= {};
$curr_ref = $curr_ref->{$_};
}
$curr_ref->{#strings[-1]} = 1;
}
You have to use hash references to walk down thru the list of keys.
use Data::Dumper;
my %hash = ();
while( my $string = <DATA> ){
chomp $string;
my #keys = split /\//, $string;
my $hash_ref = \%hash;
for my $key ( #keys ){
$hash_ref->{$key} = {};
$hash_ref = $hash_ref->{$key};
}
}
say Dumper \%hash;
__DATA__
foo/bar/some/more
also/some/stuff
this/can/have/way/too/many/substrings
Just use a library.
use Data::Diver qw(DiveVal);
my #strings = (
undef,
'foo/bar/some/more',
'also/some/stuff',
'this/can/have/way/too/many/substrings',
);
my %hash;
for my $index (1..3) {
my $root = {};
DiveVal($root, split '/', $strings[$index]) = 1;
%hash = (%hash, %$root);
}
__END__
(
also => {some => {stuff => 1}},
foo => {bar => {some => {more => 1}}},
this => {can => {have => {way => {too => {many => {substrings => 1}}}}}},
)
I took the easy way out w/'eval':
use Data::Dumper;
%hash = ();
#strings = ( 'this/is/a/path', 'and/another/path', 'and/one/final/path' );
foreach ( #strings ) {
s/\//\}\{/g;
$str = '{' . $_ . '}'; # version 2: remove this line, and then
eval( "\$hash$str = 1;" ); # eval( "\$hash{$_} = 1;" );
}
print Dumper( %hash )."\n";

Adding lists of key/values to an existing hash

I can initialize a hash from a list, e.g:
my %hash = (1..10);
my %hash2 = split /[\n=]/;
Is there a better (briefer) way to add lists of new key/values to a hash than using temporary variables?
while (<>) {
my ($k, $v) = split /=/, $_, 2;
$hash{$k} = $v;
}
It's possible, yes. It's rather like adding some numbers to an existing total - you might accomplish that like this:
my $total = 1 + 2 + 3; # here's the existing total
# let's add some additional numbers
$total = $total + 4 + 5; # the $total variable appears both
# left and right of the equals sign
You can do similar when inserting values to an existing hash...
my %hash = (a => 1, b => 2); # here's the existing hash.
# let's add some additional values
$_ = "c=3" . "\n" . "d=4";
%hash = (%hash, split /[\n=]/); # the %hash variable appears both
# left and right of the equals sign
With addition, there's a nice shortcut for this:
$total += 4 + 5;
With inserting values to a hash, there's no such shortcut.
Maybe I'm wrong but the next
use 5.014;
use warnings;
use Data::Dumper;
#create a hash
my %hash = map { "oldvar" . $_ => "oldval" . $_ } (1..3);
#add to the hash
%hash = (%hash, map {split /[=\n]/} <DATA>);
say Dumper \%hash;
__DATA__
var1=val1
var2=val2
var3=val3
prints
$VAR1 = {
'oldvar1' => 'oldval1',
'oldvar2' => 'oldval2',
'var1' => 'val1',
'var3' => 'val3',
'oldvar3' => 'oldval3',
'var2' => 'val2'
};

Sort a hash as keys in an array

I have a hash
%value
g=>10
i=>55
k=>4
n=>100
I have an array
#letters = ('k','i','n','g')
please let me know how to sort my hash in the order of keys in the array.
If you want to print hash values in order in which they appear in #letters array then,
print join ",", #value{#letters};
Code:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use Tie::IxHash;
my %hash = (
g=>10,
i=>55,
k=>4,
n=>100,
);
my %sorted_hash;
tie %sorted_hash, "Tie::IxHash";
my #array = ('k','i','n','g');
foreach(#array)
{
if(defined($hash{$_}))
{
$sorted_hash{$_} = $hash{$_};
}
}
print Dumper(%sorted_hash);
Prints:
$VAR1 = 'k';
$VAR2 = 4;
$VAR3 = 'i';
$VAR4 = 55;
$VAR5 = 'n';
$VAR6 = 100;
$VAR7 = 'g';
$VAR8 = 10;
Mention, that I used Tie::ixHash module. Otherwise, Perl won't keep array keys sorted.
This Perl module implements Perl hashes that preserve the order in which the hash elements were added
http://metacpan.org/pod/Tie::IxHash

sum hash of hash values using perl

I have a Perl script that parses an Excel file and does the following : It counts for each value in column A, the number of elements it has in column B, the script looks like this :
use strict;
use warnings;
use Spreadsheet::XLSX;
use Data::Dumper;
use List::Util qw( sum );
my $col1 = 0;
my %hash;
my $excel = Spreadsheet::XLSX->new('inout_chartdata_ronald.xlsx');
my $sheet = ${ $excel->{Worksheet} }[0];
$sheet->{MaxRow} ||= $sheet->{MinRow};
my $count = 0;
# Iterate through each row
foreach my $row ( $sheet->{MinRow}+1 .. $sheet->{MaxRow} ) {
# The cell in column 1
my $cell = $sheet->{Cells}[$row][$col1];
if ($cell) {
# The adjacent cell in column 2
my $adjacentCell = $sheet->{Cells}[$row][ $col1 + 1 ];
# Use a hash of hashes
$hash{ $cell->{Val} }{ $adjacentCell->{Val} }++;
}
}
print "\n", Dumper \%hash;
The output looks like this :
$VAR1 = {
'13' => {
'klm' => 1,
'hij' => 2,
'lkm' => 4,
},
'12' => {
'abc' => 2,
'efg' => 2
}
};
This works great, my question is : How can I access the elements of this output $VAR1 in order to do : for value 13, klm + hij = 3 and get a final output like this :
$VAR1 = {
'13' => {
'somename' => 3,
'lkm' => 4,
},
'12' => {
'abc' => 2,
'efg' => 2
}
};
So basically what I want to do is loop through my final hash of hashes and access its specific elements based on a unique key and finally do their sum.
Any help would be appreciated.
Thanks
I used #do_sum to indicate what changes you want to make. The new key is hardcoded in the script. Note that the new key is not created if no key exists in the subhash (the $found flag).
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my %hash = (
'13' => {
'klm' => 1,
'hij' => 2,
'lkm' => 4,
},
'12' => {
'abc' => 2,
'efg' => 2
}
);
my #do_sum = qw(klm hij);
for my $num (keys %hash) {
my $found;
my $sum = 0;
for my $key (#do_sum) {
next unless exists $hash{$num}{$key};
$sum += $hash{$num}{$key};
delete $hash{$num}{$key};
$found = 1;
}
$hash{$num}{somename} = $sum if $found;
}
print Dumper \%hash;
It sounds like you need to learn about Perl References, and maybe Perl Objects which are just a nice way to deal with references.
As you know, Perl has three basic data-structures:
Scalars ($foo)
Arrays (#foo)
Hashes (%foo)
The problem is that these data structures can only contain scalar data. That is, each element in an array can hold a single value or each key in a hash can hold a single value.
In your case %hash is a Hash where each entry in the hash references another hash. For example:
Your %hash has an entry in it with a key of 13. This doesn't contain a scalar value, but a references to another hash with three keys in it: klm, hij, and lkm. YOu can reference this via this syntax:
${ hash{13} }{klm} = 1
${ hash{13} }{hij} = 2
${ hash{13} }{lkm} = 4
The curly braces may or may not be necessary. However, %{ hash{13} } references that hash contained in $hash{13}, so I can now reference the keys of that hash. You can imagine this getting more complex as you talk about hashes of hashes of arrays of hashes of arrays. Fortunately, Perl includes an easier syntax:
$hash{13}->{klm} = 1
%hash{13}->{hij} = 2
%hash{13}->{lkm} = 4
Read up about hashes and how to manipulate them. After you get comfortable with this, you can start working on learning about Object Oriented Perl which handles references in a safer manner.

Perl map - need to map an array into a hash as arrayelement->array_index

I have a array like this:
my #arr = ("Field3","Field1","Field2","Field5","Field4");
Now i use map like below , where /DOSOMETHING/ is the answer am seeking.
my %hash = map {$_ => **/DOSOMETHING/** } #arr
Now I require the hash to look like below:
Field3 => 0
Field1 => 1
Field2 => 2
Field5 => 3
Field4 => 4
Any help?
%hash = map { $arr[$_] => $_ } 0..$#arr;
print Dumper(\%hash)
$VAR1 = {
'Field4' => 4,
'Field2' => 2,
'Field5' => 3,
'Field1' => 1,
'Field3' => 0
};
my %hash;
#hash{#arr} = 0..$#arr;
In Perl 5.12 and later you can use each on an array to iterate over its index/value pairs:
use 5.012;
my %hash;
while(my ($index, $value) = each #arr) {
$hash{$value} = $index;
}
Here's one more way I can think of to accomplish this:
sub get_bumper {
my $i = 0;
sub { $i++ };
}
my $bump = get_bumper; # $bump is a closure with its very own counter
map { $_ => $bump->(); } #arr;
As with many things that you can do in Perl: Don't do this. :) If the sequence of values you need to assign is more complex (e.g. 0, 1, 4, 9, 16... or a sequence of random numbers, or numbers read from a pipe), it's easy to adapt this approach to it, but it's generally even easier to just use unbeli's approach. The only advantage of this method is that it gives you a nice clean way to provide and consume arbitrary lazy sequences of numbers: a function that needs a caller-specified sequence of numbers can just take a coderef as a parameter, and call it repeatedly to get the numbers.
A very old question, but i had the same problem and this is my solution:
use feature ':5.10';
my #arr = ("Field3","Field1","Field2","Field5","Field4");
my %hash = map {state $i = 0; $_ => $i++} #arr;