Loosing some values in perl script - perl

I have several hashes. Below have value
$data->{reports}->{$port}->{tb}->{tb});
$data->{reports}->{$port}->{tb}->{change});
$data->{reports}->{$port}->{tb_pbd}->{tb_pbd});
The values are:
$VAR1 = {
'4|EXPENSES|Net Income' => '8658617.49'
};
$VAR1 = {
'4|EXPENSES|Net Income' => '8728605.17'
};
$VAR1 = {
'4|EXPENSES|Net Income' => '-69987.68'
};
separate variable has value:
$keyee
value = 1|ASSETS|11240-000
However, when I put this value to all hash, for example:
$data->{reports}->{$port}->{tb}->{tb}->{$keyee}
It became undef. Any idea why and how to make treatment?

A hash key can hold only one value. To store multiple values associated with a single key , you need to use hash of array.
Code example :
my %myhash;
$myhash{'tb'}="val1";
$myhash{'tb'} ="vale2";
In the above example tb key will have only one value.
Now If you want to get both the values associated to the tb key use hash of array .
code example
push(#{$myhash},"val1");
push(#{$myhash},"val2");
Hope the above concept will help you.
For more information about hash of array visit : http://docstore.mik.ua/orelly/perl2/prog/ch09_02.htm

Related

Raku: Trouble Accessing Value of a Multidimensional Hash

I am having issues accessing the value of a 2-dimensional hash. From what I can tell online, it should be something like: %myHash{"key1"}{"key2"} #Returns value
However, I am getting the error: "Type Array does not support associative indexing."
Here's a Minimal Reproducible Example.
my %hash = key1-dim1 => key1-dim2 => 42, key2-dim1 => [42, 42];
say %hash{'key1-dim1'}{'key1-dim2'}; # 42
say %hash{'key2-dim1'}{'foo bar'}; # Type Array does not support associative indexing.
Here's another reproducible example, but longer:
my #tracks = 'Foo Bar', 'Foo Baz';
my %count;
for #tracks -> $title {
$_ = $title;
my #words = split(/\s/, $_);
if (#words.elems > 1) {
my $i = 0;
while (#words.elems - $i > 1) {
my %wordHash = ();
%wordHash.push: (#words[$i + 1] => 1);
%counts.push: (#words[$i] => %wordHash);
say %counts{#words[$i]}{#words[$i+1]}; #===============CRASHES HERE================
say %counts.kv;
$i = $i + 1;
}
}
}
In my code above, the problem line where the 2-d hash value is accessed will work once in the first iteration of the for-loop. However, it always crashes with that error on the second time through. I've tried replacing the array references in the curly braces with static key values in case something was weird with those, but that did not affect the result. I can't seem to find what exactly is going wrong by searching online.
I'm very new to raku, so I apologize if it's something that should be obvious.
After adding the second elements with push to the same part of the Hash, the elment is now an array. Best you can see this by print the Hash before the crash:
say "counts: " ~ %counts.raku;
#first time: counts: {:aaa(${:aaa(1)})}
#second time: counts: {:aaa($[{:aaa(1)}, {:aaa(1)}])}
The square brackets are indicating an array.
Maybe BagHash does already some work for you. See also raku sets without borders
my #tracks = 'aa1 aa2 aa2 aa3', 'bb1 bb2', 'cc1';
for #tracks -> $title {
my $n = BagHash.new: $title.words;
$n.raku.say;
}
#("aa2"=>2,"aa1"=>1,"aa3"=>1).BagHash
#("bb1"=>1,"bb2"=>1).BagHash
#("cc1"=>1).BagHash
Let me first explain the minimal example:
my %hash = key1-dim1 => key1-dim2 => 42,
key2-dim1 => [42, 42];
say %hash{'key1-dim1'}{'key1-dim2'}; # 42
say %hash{'key2-dim1'}{'key2-dim2'}; # Type Array does not support associative indexing.
The problem is that the value associated with key2-dim1 isn't itself a hash but is instead an Array. Arrays (and all other Positionals) only support indexing by position -- by integer. They don't support indexing by association -- by string or object key.
Hopefully that explains that bit. See also a search of SO using the [raku] tag plus 'Type Array does not support associative indexing'.
Your longer example throws an error at this line -- not immediately, but eventually:
say %counts{...}{...}; # Type Array does not support associative indexing.
The hash %counts is constructed by the previous line:
%counts.push: ...
Excerpting the doc for Hash.push:
If a key already exists in the hash ... old and new value are both placed into an Array
Example:
my %h = a => 1;
%h.push: (a => 1); # a => [1,1]
Now consider that the following code would have the same effect as the example from the doc:
my %h;
say %h.push: (a => 1); # {a => 1}
say %h.push: (a => 1); # {a => [1,1]}
Note how the first .push of a => 1 results in a 1 value for the a key of the %h hash, while the second .push of the same pair results in a [1,1] value for the a key.
A similar thing is going on in your code.
In your code, you're pushing the value %wordHash into the #words[$i] key of the %counts hash.
The first time you do this the resulting value associated with the #words[$i] key in %counts is just the value you pushed -- %wordHash. This is just like the first push of 1 above resulting in the value associated with the a key, from the push, being 1.
And because %wordHash is itself a hash, you can associatively index into it. So %counts{...}{...} works.
But the second time you push a value to the same %counts key (i.e. when the key is %counts{#words[$i]}, with #words[$i] set to a word/string/key that is already held by %counts), then the value associated with that key will not end up being associated with %wordHash but instead with [%wordHash, %wordHash].
And you clearly do get such a second time in your code, if the #tracks you are feeding in have titles that begin with the same word. (I think the same is true even if the duplication isn't the first word but instead later ones. But I'm too confused by your code to be sure what the exact broken combinations are. And it's too late at night for me to try understand it, especially given that it doesn't seem important anyway.)
So when your code then evaluates %counts{#words[$i]}{#words[$i+1]}, it is the same as [%wordHash, %wordHash]{...}. Which doesn't make sense, so you get the error you see.
Hopefully the foregoing has been helpful.
But I must say I'm both confused by your code, and intrigued as to what you're actually trying to accomplish.
I get that you're just learning Raku, and that what you've gotten from this SO might already be enough for you, but Raku has a range of nice high level hash like data types and functionality, and if you describe what you're aiming at we might be able to help with more than just clearing up Raku wrinkles that you and we have been dealing with thus far.
Regardless, welcome to SO and Raku. :)
Well, this one was kind of funny and surprising. You can't go wrong if you follow the other question, however, here's a modified version of your program:
my #tracks = ['love is love','love is in the air', 'love love love'];
my %counts;
for #tracks -> $title {
$_ = $title;
my #words = split(/\s/, $_);
if (#words.elems > 1) {
my $i = 0;
while (#words.elems - $i > 1) {
my %wordHash = ();
%wordHash{#words[$i + 1]} = 1;
%counts{#words[$i]} = %wordHash;
say %counts{#words[$i]}{#words[$i+1]}; # The buck stops here
say %counts.kv;
$i = $i + 1;
}
}
}
Please check the line where it crashed before. Can you spot the difference? It was kind of a (un)lucky thing that you used i as a loop variable... i is a complex number in Raku. So it was crashing because it couldn't use complex numbers to index an array. You simply had dropped the $.
You can use sigilless variables in Raku, as long as they're not i, or e, or any of the other constants that are already defined.
I've also made a couple of changes to better reflect the fact that you're building a Hash and not an array of Pairs, as Lukas Valle said.

Difference between a direct perl hash reference and a hash that is turned into a reference

I am trying to understand an example of code given here: https://www.perlmonks.org/?node_id=1083257 and the difference between the directly created hash references given in the example and one that I alternatively create first as a hash. When I run the following code:
use strict;
use warnings;
use Algorithm::NaiveBayes;
my $positive = {
remit => 2,
due => 4,
within => 1,
};
my $negative = {
assigned => 3,
secured => 1,
};
my $categorizer = Algorithm::NaiveBayes->new;
$categorizer->add_instance(
attributes => $positive,
label => 'positive');
$categorizer->add_instance(
attributes => $negative,
label => 'negative');
$categorizer->train;
my $sentence1 = {
due => 2,
remit => 1,
};
my $probability = $categorizer->predict(attributes => $sentence1);
print "Probability positive: $probability->{'positive'}\n";
print "Probability negative: $probability->{'negative'}\n";
I get the result:
Probability positive: 0.999500937781821
Probability negative: 0.0315891654410057
However, when I try to create the hash reference in the following way:
my %sentence1 = {
"due", 2,
"remit", 1
};
my $probability = $categorizer->predict(attributes => \%sentence1);
I get:
Reference found where even-sized list expected at simple_NaiveBayes.pl line 57.
Probability positive: 0.707106781186547
Probability negative: 0.707106781186547
Why is my hash \%sentence1 different from the $sentence1 hash reference given in the example?
Just like the warning says, you are assigning a reference where an even-sized list was expected. This
my %sentence1 = { # curly bracers create a hash reference, a scalar value
"due", 2,
"remit", 1
};
Should look like this
my %sentence1 = ( # parentheses used to return a list (*)
"due", 2,
"remit", 1
);
(*): Parentheses do not actually create a list, they just override precedence. In this case of = over , or =>, which returns a list of items to the assignment.
Or this
my $sentence1 = { # scalar variable is fine for a reference
"due", 2,
"remit", 1
};
If you want to get technical, what is happening in your hash assignment is that the hash reference { ... } is stringified and turned into a hash key. (The string will be something like HASH(0x104a7d0)) The value of that hash key will be undef, because the "list" you assigned to the hash only contained 1 thing: The hash reference. (Hence the "even-sized list" warning) So if you print such a hash with Data::Dumper, you would get something that looks like this:
$VAR1 = {
'HASH(0x104a7d0)' => undef
};
my %sentence1 = {
"due", 2,
"remit", 1
};
You did this wrong (you tried to create a hash with one key, which is a hashref (which doesn't work) and no corresponding value). That's why perl gave you a warning about finding a reference instead of an even-sized list. You wanted
my %sentence1 = (
"due", 2,
"remit", 1
);

Initialize empty Array of Hashes of a given length - one-liner

I'd like to pre-initialize the elements of an array of hashes so that when it comes time to filling in the data, I don't need to check for the existence of various members and initialize them every loop. If I can, I'd like to pre-initialize the general form of the datastructure which should look like this:
$num_sections = 4;
# Some magic to initialize $VAR1 to this:
$VAR1 = {
'sections' => [
{},
{},
{},
{}
]
};
I would like this to work,
$ph->{sections} ||= [({} x $num_sections )];
but it results in
$VAR1 = {
'sections' => 'HASH(0x21b6110)HASH(0x21b6110)HASH(0x21b6110)HASH(0x21b6110)'
};
And no amount of playing with the () list context and {} empty hash reference seem to make it work.
This works but it's not quite a one-liner
unless ($ph->{sections})
{
push #{ $ph->{sections}}, {} foreach (1..$num_sections);
}
There's probably some perl magic that I can use to add the unless to the end, but I haven't quite figured it out.
I feel I'm so close, but I just can't quite get it.
Update Oleg points out that this probably isn't necessary at all. See comments below.
If the left-hand side of x is not in parens, x repeats the string on its LHS and returns the concatenation of those strings.
If the left-hand side of x is in parens, x repeats the value on its LHS and returns the copies.
This latter approach is closer to what you want, but it's still wrong as you'll end up with multiple references to a single hash. You want to create not only new references, but new hashes as well. For that, you can use the following:
$ph->{sections} ||= [ map { +{} } 1..$num_sections ];

Perl array of hash structures

This is a design setup question. I know in Perl there are not array of arrays. I am looking at reading in code that pulls in data from large text files at phases of something in flight. Each of these phases track different variables (and different numbers of them) . I have to store them because in the second part of the script i am rewriting them into another file I am updating as I read in.
I thought first I should have an array of hash's, however the variables are not the same at each phase. Then I thought maybe and array with the name of several arrays (array of references I guess) .
Data example would be similar to
phase 100.00 mass 0.9900720175260495E+005
phase 240.00 gcrad 61442116.0 long 0.963710076E+003 gdalt 0.575477727E+002 vell 0.9862937759999998E+002
Data is made up but you should get the idea and there would be many phases and the variable would likely range from 1 to 25 variables in each phase
You can use Arrays of Arrays in Perl. You can find documentation on Perl data structures including Arrays of Arrays here: http://perldoc.perl.org/perldsc.html. That said, looking at the sample you've provided it looks like what you need is an Array of Hashes. Perhaps something like this:
my #data = (
{ phase => 100.00,
mass => 0.9900720175260495e005 },
{ phase => 240.00
gcrad => 61442116.0
long => 0.963710076e003
gdalt => 0.575477727e002
vell => 0.9862937759999998e002 }
);
to access the data you would use:
$data[0]->{phase} # => 100.00
You could also use a Hash of Hashes like this:
my %data = (
name1 => {
phase => 100.00,
mass => 0.9900720175260495e005
},
name2 => {
phase => 240.00
gcrad => 61442116.0
long => 0.963710076e003
gdalt => 0.575477727e002
vell => 0.9862937759999998e002
}
);
to access the data you would use:
$data{name1}->{phase} # => 100.00
A great resource for learning how to implement advanced data structures and algorithms in Perl is the book, Mastering Algorithms in Perl
I use the following mnemonic when defining arrays, array references and hash references:
Use parentheses for lists -- lists can be assigned to either arrays or hashes:
my %person = (
given_name => 'Zaphod',
surname => 'Beeblebrox'
);
or
my #rainbow = (
'red',
'orange',
'yellow',
'green',
'blue',
'indigo',
'violet'
);
Because the lists are assigned to list types -- array and hash, there is no semantic ambiguity. When you deal with array references or hash references, however, the delimiter must distinguish between the types of reference, because the $ sigil for scalar variables can't be used to distinguish between the two types of reference. Therefore, [] is used to denote array references, just as [] is used to dereference arrays, and {} is used to denote hash references, just as {} is used to dereference hashes.
So an array of arrayrefs looks like this:
my #AoA = (
[1,2,3],
[4,5,6],
[7,8,9]
);
An array of hashrefs:
my #AoH = (
{ given_name => 'Ford', surname => 'Prefect' },
{ given_name => 'Arthur', surname => 'Dent' }
);
A hashref assigned to a scalar variable:
my $bones = {
head => 'skull',
jaw => 'mandible',
'great toe' => 'distal phalanx'
};

Modifying hash within a hash in Perl

What is the shortest amount of code to modify a hash within a hash in the following instances:
%hash{'a'} = { 1 => one,
2 => two };
(1) Add a new key to the inner hash of 'a' (ex: c => 4 in the inner hash of 'a')
(2) Changing a value in the inner hash (ex: change the value of 1 to 'ONE')
Based on the question, you seem new to perl, so you should look at perldoc perlop among others.
Your %hash keys contain scalar values that are hashrefs. You can dereference using the -> operator, eg, $hashref = {foo=>42}; $hashref->{foo}. Similarly you can do the same with the values in the hash: $hash{a}->{1}. When you chain the indexes, though, there's some syntactic sugar for an implicit -> between them, so you can just do $hash{a}{1} = 'ONE' and so on.
This question probably also will give you some useful leads.
$hash{a}{c} = 4;
$hash{a}{1} = "ONE";