How can multiple hash values be retrieved using perl? - perl

How can multiple hash values be retrieved? I tried using
use Hash::MultiValue and get_all(). It throws an error saying "Can't call method "get_all" on an undefined value" . Which is the better option to implement this functionality of multiple values for a particular key ? The value of the key is the file that is being opened.
use warnings;
use List::MoreUtils qw(firstidx);
use Hash::MultiValue;
my $key_in;
…
open ($FR, "<$i") or die "couldn't open list";
while($line=<$FR>){
if($line =~ /search_pattern/){
my $idx = firstidx { $_ eq 'hash_key' } #tags;
my $key= #tags[$idx+1];
$hash{$key}= Hash::MultiValue->new($key=>'$i');
}
close($FR);
for my $key_in ( sort keys %hash ) {
#key_in = $hash->get_all('$key_in');
print "$key_in = $hash{$key_in}\n";
}
my $key_in = <STDIN>;
if (exists($hash{$key_in})){
$hash_value = $hash{$key_in};
}else{
exit;
}

I think you want an array reference for the value. You can then treat that as an array. This is the sort of stuff we show you in Intermediate Perl:
$hash{$key} = [];
push #{ $hash{$key} }, $some_value;
my #values = #{ $hash{$key} };
With Perl v5.24, you can use postfix dereferencing to make it a bit prettier:
use v5.24;
$hash{$key} = [];
push $hash{$key}->#*, 'foo';
push $hash{$key}->#*, 'bar';
my #values = $hash{$key}->#*;
And, since Perl automatically takes an undefined value and turns it into the reference structure you need (auto vivification), you don't need to initialize an undefined value:
use v5.24;
push $hash{$key}->#*, 'foo';
push $hash{$key}->#*, 'bar';
my #values = $hash{$key}->#*;
Get all the keys of a hash:
my #keys = keys %hash;
Get all of the values (in the order of the corresponding keys if you haven't changed the hash since you called keys):
my #values = values %keys;
Get some values with a hash slice:
my #some_values = #hash{#some_keys};
Get some keys and values (key-value slice):
use v5.20;
my %smaller_hash = %hash{#some_keys}

Here is an example of how you can use get_all() from Hash::MultiValue to retrive multiple hash values for a given key:
use strict;
use warnings;
use Data::Dumper qw(Dumper);
use Hash::MultiValue;
my $hash = Hash::MultiValue->new();
$hash->add(tag1 => 'file1');
$hash->add(tag1 => 'file2');
$hash->add(tag2 => 'file3');
my #foo = $hash->get_all('tag1');
print(Dumper(\#foo));
Output:
$VAR1 = [
'file1',
'file2'
];

Related

Extracting and storing the the values in key value pair from a text in file in perl

I have a text file like which contains information like this:
name=A
class=B
RollNo=C
I want to extract the values in perl script
key(name) = value(A)
key(class) = value(B)
key(RollNo) = value(C)
the keys should be exported as the variables which will have values. Whenever we type
print $name
the output should be 'A'
I have tried:
open my $fh, '<', $file_name
or die "Could not open sample.txt: $!";
my #lines = <$fh>;
my %hash;
while (<#lines>) {
chomp;
my ($key, $value) = split /=/;
next unless defined $value;
$hash{$key} = $value;
}
print %hash;
Your code looks pretty good and most of what you've done so far works.
At the end, you run print %hash and that doesn't give you what you expect. That will "unroll" the keys and values from the hash into a list and print that list. So you get all of the keys and values printed out.
If you just want one value (for example, the value associated with the "name" key), then just print that.
print $hash{name};
Is that what you were looking for?
You could try using one of the configuration modules that are available. Config::Tiny seems to fit your data:
use strict;
use warnings;
use Data::Dumper;
use Config::Tiny;
my $Config = Config::Tiny->new;
$Config = Config::Tiny->read( 'a.txt' ); # your text file name goes here
print $Config->{_}{name}; # print the name value
print Dumper $Config; # print all the values in perl variable format
You can store data in hash and can retrieve from their.
use strict;
use warnings;
use Data::Dumper;
my %hash = (
name => 'A',
class => 'B',
RollNo => 'C'
);
print Dumper(\%hash);
print $hash{'name'};

Passing a Perl Hash by reference plus scalar variable

I'm trying to pass from Main.pl to a sub (in ReadConfigFile.pm) a hash by reference and a scalar value. The scalar variable is the path to a config file and once this file is opened I want to fill a hash with some of its values. How do i pass a hash by reference and a scalar so that I then have the hash values available to use in Main.pl
I have done a lot of reading but cant get this to work. I realise i cant do = #_; in my sub as that is creating a new hash.
Ive tried following the prototype method, this fills the hash ok but back in Main.pl the hash is empty.
Main.pl
# Read the config file. Return 3 scalars and a hash
my %apps;
my ($schema, $directory, $staticFile) = readConfigFile(\%apps, $configFilePath);
my %app_list = %apps; # ive tried this in, out and in a variety of states
foreach my $name (sort keys %app_list) {
print "\nMAIN $name";
}
# this is empty
ReadConfigFile.pm
sub readConfigFile (\%$) {
my ($apps_ref, $configFilePath) = #_;
# also tried
# $apps_ref = shift but then configFilePath is empty
# linearray is each line from open config file split by :
$apps_ref{$lineArray[1]}{id} = $lineArray[1];
$apps_ref{$lineArray[1]}{name} = $lineArray[2];
$schema = $lineArray[1];
$directory = $lineArray[1];
$staticFile = $lineArray[1];
return ($schema, $directory, $staticFile);
configFile.txt
APP:1101:ACTIVITY
APP:1102:EVENTS
APP:1103:PERFORMANCE
APP:1104:LOCATION
STATIC_FILE:static_file.sql
SCHEMA:CAASS
DIRECTORY:CAASS
I want to get the 3 scalar variable returned and the hash so i can use them throughout Main.pl and pass to other subs.
I have also tried passing in just the configfilename and returning 4 variables, the 3 scalars and the hash.
I expect someone will crack this in minutes but i just cant work out the combination of \ and # and % and $ to make it work.
Thanks for any help or ideas.
Edit 1:
Main.pl
my %apps;
my ($schema, $directory, $staticFile) = readConfigFile(\%apps, $configFilePath);
foreach my $name (sort keys %apps) {
print "\nMAIN $name";
}
ReadConfigFile
sub readConfigFile () {
my $apps_ref = shift;
my $configFilePath = $_[0];
#Fill It
$apps_ref{$lineArray[1]}{id} = $lineArray[1];
$apps_ref{$lineArray[1]}{name} = $lineArray[2];
# This shows results
foreach my $name (sort keys %apps_ref) {
print "\nreadConfigFile $name";
}
But the values arent coming back into Main.pl
edit 2:
So im still interested in how the above can be made to work. But ive attacked it a different way and it works
Main.pl
my ($schema, $directory, $staticFile, %apps) = readConfigFile($configFilePath);
foreach my $name (sort keys %apps) {
print "\nMAIN $name";
}
ReadConfigFile
sub readConfigFile () {
my $configFilePath = $_[0];
my %apps;
#Fill It
%apps{$lineArray[1]}{id} = $lineArray[1];
$apps{$lineArray[1]}{name} = $lineArray[2];
foreach my $name (sort keys %apps) {
print "\nreadConfigFile $name";
}
return ($schema, $directory, $staticFile, %apps);
Both sets off output show.
There is no implicit 'pass by reference' in Perl*. Everything is passed the same way - as a list of scalars, by alias (thus passing a hash itself will instead pass the list of its keys and values*). But you can create a reference, pass it, and then dereference it to use it - and references can be copied around without copying the underlying structure.
use strict;
use warnings;
my %hash;
my $ref = \%hash;
my $copy = $ref;
$copy->{a} = 1;
print "$ref->{a}\n"; # also 1
References will maintain their referenced structure after a my (...) = #_; or my $foo = shift; assignment in a subroutine.
use strict;
use warnings;
sub foo {
my ($ref, $key) = #_;
$ref->{$key} = 42;
}
my %hash;
foo(\%hash, 'foo');
print "$hash{foo}\n"; # 42
See https://p3rl.org/REF for the relevant documentation on Perl references.
Since you are already passing a reference, there is no need for your (\%$) prototype: you can just remove it from the subroutine definition.
*except sort of with prototypes, but it's better to avoid them in most cases.

Processing array of hashrefs correctly

I have an list of maps in my code.
my #codeList;
push (#codeList, \%map1, \%map2, \%map3);
when I am trying to access it via looping over List index it is not giving me proper map.
what am I missing.
my $Count = #codeList;
for (my $index =0; $index < $Count; $index++)
{
my %map = $codeList[$index];
}
Instead of
my %map = $dbColsList[$dbCount];
you have to use reference as #codeList was populated with them => \%map1
my $map = $dbColsList[$dbCount];
and later use it like $map->{key} as this is array of hashes or hashref structure.
Check perldoc for details.
Alternatively you can dereference hashref and do a shallow copy (changes to %map keys/values won't reflect on \%map1, etc.)
my %map = %{ $dbColsList[$dbCount] };
Your code works fine. i think its your loop ( which you did'nt show us). You can loop through this by dereferencing the hashref (%{ $hashref }):
use strict;
use warnings;
use feature 'say';
my %map1 = (test1 => 'ab');
my %map2 = (test2 => 'ab');
my %map3 = (test2 => 'ab');
my #codeList;
push (#codeList, \%map1, \%map2, \%map3);
for my $hashref (#codeList) {
for my $key (keys %{$hashref}) {
say $key . q{ } . $hashref->{$key};
}
}
EDIT Output:
test1 ab
test2 ab
test2 ab

Build hash of hash in perl

I'm new to using perl and I'm trying to build a hash of a hash from a tsv. My current process is to read in a file and construct a hash and then insert it into another hash.
my %hoh = ();
while (my $line = <$tsv>)
{
chomp $line;
my %hash;
my #data = split "\t", $line;
my $id;
my $iter = each_array(#columns, #data);
while(my($k, $v) = $iter->())
{
$hash{$k} = $v;
if($k eq 'Id')
{
$id = $v;
}
}
$hoh{$id} = %hash;
}
print "dump: ", Dumper(%hoh);
This outputs:
dump
$VAR1 = '1234567890';
$VAR2 = '17/32';
$VAR3 = '1234567891';
$VAR4 = '17/32';
.....
Instead of what I would expect:
dump
{
'1234567890' => {
'k1' => 'v1',
'k2' => 'v2',
'k3' => 'v3',
'k4' => 'v4',
'id' => '1234567890'
},
'1234567891' => {
'k1' => 'v1',
'k2' => 'v2',
'k3' => 'v3',
'k4' => 'v4',
'id' => '1234567891'
},
........
};
My limited understanding is that when I do $hoh{$id} = %hash; its inserting in a reference to %hash? What am I doing wrong? Also is there a more succint way to use my columns and data array's as key,value pairs into my %hash object?
-Thanks in advance,
Niru
To get a reference, you have to use \:
$hoh{$id} = \%hash;
%hash is the hash, not the reference to it. In scalar context, it returns the string X/Y wre X is the number of used buckets and Y the number of all the buckets in the hash (i.e. nothing useful).
To get a reference to a hash variable, you need to use \%hash (as choroba said).
A more succinct way to assign values to columns is to assign to a hash slice, like this:
my %hoh = ();
while (my $line = <$tsv>)
{
chomp $line;
my %hash;
#hash{#columns} = split "\t", $line;
$hoh{$hash{Id}} = \%hash;
}
print "dump: ", Dumper(\%hoh);
A hash slice (#hash{#columns}) means essentially the same thing as ($hash{$columns[0]}, $hash{$columns[1]}, $hash{$columns[2]}, ...) up to however many columns you have. By assigning to it, I'm assigning the first value from split to $hash{$columns[0]}, the second value to $hash{$columns[1]}, and so on. It does exactly the same thing as your while ... $iter loop, just without the explicit loop (and it doesn't extract the $id).
There's no need to compare each $k to 'Id' inside a loop; just store it in the hash as a normal field and extract it afterwards with $hash{Id}. (Aside: Is your column header Id or id? You use Id in your loop, but id in your expected output.)
If you don't want to keep the Id field in the individual entries, you could use delete (which removes the key from the hash and returns the value):
$hoh{delete $hash{Id}} = \%hash;
Take a look at the documentation included in Perl. The command perldoc is very helpful. You can also look at the Perldoc webpage too.
One of the tutorials is a tutorial on Perl references. It all help clarify a lot of your questions and explain about referencing and dereferencing.
I also recommend that you look at CPAN. This is an archive of various Perl modules that can do many various tasks. Look at Text::CSV. This module will do exactly what you want, and even though it says "CSV", it works with tab separated files too.
You missed putting a slash in front of your hash you're trying to make a reference. You have:
$hoh{$id} = %hash;
Probably want:
$hoh{$id} = \%hash;
also, when you do a Data::Dumper of a hash, you should do it on a reference to a hash. Internally, hashes and arrays have similar structures when a Data::Dumper dump is done.
You have:
print "dump: ", Dumper(%hoh);
You should have:
print "dump: ", Dumper( \%hoh );
My attempt at the program:
#! /usr/bin/env perl
#
use warnings;
use strict;
use autodie;
use feature qw(say);
use Data::Dumper;
use constant {
FILE => "test.txt",
};
open my $fh, "<", FILE;
#
# First line with headers
#
my $line = <$fh>;
chomp $line;
my #headers = split /\t/, $line;
my %hash_of_hashes;
#
# Rest of file
#
while ( my $line = <$fh> ) {
chomp $line;
my %line_hash;
my #values = split /\t/, $line;
for my $index ( ( 0..$#values ) ) {
$line_hash{ $headers[$index] } = $values[ $index ];
}
$hash_of_hashes{ $line_hash{id} } = \%line_hash;
}
say Dumper \%hash_of_hashes;
You should only store a reference to a variable if you do so in the last line before the variable goes go of scope. In your script, you declare %hash inside the while loop, so placing this statement as the last in the loop is safe:
$hoh{$id} = \%hash;
If it's not the last statement (or you're not sure it's safe), create an anonymous structure to hold the contents of the variable:
$hoh{$id} = { %hash };
This makes a copy of %hash, which is slower, but any subsequent changes to it will not effect what you stored.

How do you sort the output of Data::Dumper?

I want to dump the values of my object and hash, but it keeps printing the keys out of order. How can I dump the keys in (recursive) sort-order?
use Data::Dumper;
print Dumper $obj;
Set $Data::Dumper::Sortkeys = 1 to get Perl's default sort order.
If you want to customize the order, set $Data::Dumper::Sortkeys to a reference to a subroutine that receives a reference to a hash as input, and outputs a reference to the list of the hash's keys in the order you want them to appear.
# sort keys
$Data::Dumper::Sortkeys = 1;
print Dumper($obj);
# sort keys in reverse order - use either one
$Data::Dumper::Sortkeys = sub { [reverse sort keys %{$_[0]}] };
$Data::Dumper::Sortkeys = sub { [sort {$b cmp $a} keys %{$_[0]}] };
print Dumper($obj);
Short answer for the impatient
Use Data::Dumper::Concise instead. It sorts your keys. Use it like this:
use Data::Dumper::Concise;
my $pantsToWear = {
pony => 'jeans',
unicorn => 'corduroy',
marsupials => {kangaroo => 'overalls', koala => 'shorts + suspenders'},
};
warn Dumper($pantsToWear);
More words for the curious
Data::Dumper::Concise also gives you more compact, easier to read output.
Note that Data::Dumper::Concise is Data::Dumper with reasonable default configuration values set for you. Its equivalent to using Data::Dumper like this:
use Data::Dumper;
{
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Indent = 1;
local $Data::Dumper::Useqq = 1;
local $Data::Dumper::Deparse = 1;
local $Data::Dumper::Quotekeys = 0;
local $Data::Dumper::Sortkeys = 1;
warn Dumper($var);
}
You can set the $Data::Dumper::Sortkeys variable to a true value to get a default sort:
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
my $hashref = {
bob => 'weir',
jerry =>, 'garcia',
nested => {one => 'two', three => 'four'}};
print Dumper($hashref), "\n";
or put a subroutine in there to sort the keys however you want.
From the Data::Dumper documentation:
$Data::Dumper::Sortkeys or $OBJ->Sortkeys([NEWVAL])
Can be set to a boolean value to control whether hash keys are dumped in sorted order.
A true value will cause the keys of all hashes to be dumped in Perl's default sort order.
Can also be set to a subroutine reference which will be called for each hash that is dumped.
In this case Data::Dumper will call the subroutine once for each hash, passing it the
reference of the hash. The purpose of the subroutine is to return a reference to an array of
the keys that will be dumped, in the order that they should be dumped. Using this feature, you
can control both the order of the keys, and which keys are actually used. In other words, this
subroutine acts as a filter by which you can exclude certain keys from being dumped. Default is
0, which means that hash keys are not sorted.
For those who want to sort a hashref by value when printing it with Data::Dumper, here is an example:
$Data::Dumper::Sortkeys = sub {
# Using <=> to sort numeric values
[ sort { $_[0]->{$a} <=> $_[0]->{$b} } keys %{ $_[0] } ]
};
And here is a more readable alternative, doing the same but with a variable to hold the hash. It's less efficient, but for small hashes, some may find it nicer:
$Data::Dumper::Sortkeys = sub {
my %h = %{$_[0]};
# cmp for string comparisons
[ sort { $h{$a} cmp $h{$b} } keys %h ];
};
sort ascii and full numeric:
$Data::Dumper::Sortkeys = sub {
no warnings 'numeric';
if(join('',keys %{$_[0]})=~/\d+/)
{
[ sort { $a <=> $b } keys %{$_[0]} ]
}
else
{
return [sort(keys %{$_[0]})];
}
};