How can I convert one Perl hash to another using the keys? - perl

I've just started diving in to the crazy world that is perl and have come across a problem that I cannot seem to wrap my head around. Specifically I need to be able to convert from one hash structure to another, that uses the keys/values as the new hashes keys/values.
An example:
Input hash:
my %original_hash = (
first_key => { some_key => "apples",
another_key => "chips",
#potentially more here
},
second_key => { more_of_same => "dogs",
its_another => "cats",
#potentially more here
}
);
Output hash:
my %final_hash = (
some_key => {
apples => {
more_of_same => "dogs",
its_another => "cats",
}
} ,
another_key => {
chips => {
more_of_same => "dogs",
its_another => "cats",
}
}
);
And yes I do want the second_key's data repeated in the final_hash, as there will be an array of the original_hashes that are inputted. The first element becomes the base-case, and all other elements may append or remove from that list.
If anyone has any suggestions on how to go about doing this that would be greatly appreciated!

Here is another way
my %final_hash;
my %tmp = %{$original_hash{first_key}};
my $val = $original_hash{second_key};
while ( my ($k,$v) = each %tmp) {
$final_hash{$k} = { $v => $val };
}
print Dumper (\%final_hash);

Okay, Sinan is right, it's very hard to guess your problem, but the following code seems to do what you want ... or at least it produces the listed output.... :)
use strict;
use warnings;
use Data::Dumper;
$Data::Dumper::Deepcopy = 1;
my %original_hash = (
first_key => { some_key => "apples",
another_key => "chips",
#potentially more here
},
second_key => { more_of_same => "dogs",
its_another => "cats",
#potentially more here
}
);
my %final_hash;
for my $key ( keys %{ $original_hash{first_key} } ) {
$final_hash{$key} = {
$original_hash{first_key}->{$key}
=> $original_hash{second_key}
};
}
print Dumper(\%final_hash);

Related

executing a function within an array within a hash in perl

I have a Perl data structurte like so
%myhash = (
k1 => v1,
kArray => [
{
name => "anonymous hash",
...
},
\&funcThatReturnsHash,
{
name => "another anonymous hash",
...
}
]
);
Elsewhere I iterate through the list in kArray which contains a bunch of hashes. I would like to either process the actual hash OR the hash returned by the function.
foreach my $elem( #{myhash{kArray}} ) {
if (ref($elem) == "CODE") {
%thisHash = &$elem;
}
else {
%thisHash = %$elem;
}
...
}
However ref ($elem) is always scalar or undefined. I tried func, &func, \&func, \%{&func}, in %myhash to no effect.
how do I extract the hash within the function in the main body?
Apart from the code sample you give being invalid Perl, the main problems seem to be that you are using == to compare strings instead of eq, and you are assigning a hash reference to a hash variable %thishash. I assure you that ref $elem never returns SCALAR with the data you show
It would help you enormously if you followed the common advice to use strict and use warnings at the top of your code
This will work for you
for my $elem ( #{ $myhash{kArray} } ) {
my $this_hash;
if ( ref $elem eq 'CODE' ) {
$this_hash = $elem->();
}
else {
$this_hash = $elem;
}
# Do stuff with $this_hash
}
or you could just use a map like this
use strict;
use warnings;
use 5.010;
use Data::Dump;
my %myhash = (
k1 => v1,
kArray => [
{
name => "anonymous hash",
},
\&funcThatReturnsHash,
{
name => "another anonymous hash",
}
]
);
for my $hash ( map { ref eq 'CODE' ? $_->() : $_ } #{ $myhash{kArray} } ) {
say $hash->{name};
}
sub funcThatReturnsHash {
{ name => 'a third anonymous hash' };
}
output
anonymous hash
a third anonymous hash
another anonymous hash
If you turn on strict and warnings, you'll see that:
foreach my $elem(#{mynahs{kArray}}) {
Isn't valid. You need at the very least a $ before mynahs.
But given something like this - your approach works - here's an example using map to 'run' the code references:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
sub gimme_hash {
return { 'fish' => 'paste' };
}
my $stuff =
[ { 'anon1' => 'value' },
\&gimme_hash,
{ 'anon2' => 'anothervalue' }, ];
my $newstuff = [ map { ref $_ eq "CODE" ? $_->() : $_ } #$stuff ];
print Dumper $newstuff;
Turns that hash into:
$VAR1 = [
{
'anon1' => 'value'
},
{
'fish' => 'paste'
},
{
'anon2' => 'anothervalue'
}
];
But your approach does work:
foreach my $element ( #$stuff ) {
my %myhash;
if ( ref $element eq "CODE" ) {
%myhash = %{$element -> ()};
}
else {
%myhash = %$element;
}
print Dumper \%myhash;
}
Gives:
$VAR1 = {
'anon1' => 'value'
};
$VAR1 = {
'fish' => 'paste'
};
$VAR1 = {
'anon2' => 'anothervalue'
};

Use Mango to search MongoDB by multiple ObjectIDs

How can I find multiple MongoDB documents by their ObjectIDs using Mango in one go. I currently have a non-blocking sub which I'm using to filter out the docs that don't match in a grep statement but was wondering if it's possible to pass the ObjectIDs (maybe an array of instances of Mango::BSON::ObjectID) to the find method?
I also think it's not very efficient this way since big collections will bare a hefty price!
#items;
$self->mango->db->collection('items')->find()->all(sub {
my ($collection, $err, $items) = #_;
return $self->render_exception($err) if $err;
my #oids = $self->req->params->param('ids[]');
foreach my $item (#$items) {
push #items, $item if grep (/$item->{_id}/, #oids);
}
$self->render(json => {items => \#items});
});
This sub is inside one of my Mojolicous controllers which responds to a JSON call.
I'm using the following:
Mojolicous 4.91
Mango 0.24
MongoDB 2.4.9
Thanks in advance.
Update
I have applied Neil's logic and now it works fine.
my #oids = map { Mango::BSON::ObjectID->new($_) } ($self->req->params->param('ids[]'));
print Dumper(#oids),"\n";
$self->mango->db->collection('items')->find({ "_id" => {'$in' => \#oids} })->all(sub {
my ($collection, $err, $items) = #_;
return $self->render_exception($err) if $err;
print Dumper($items),"\n";
$self->render(json => {items => $items});
});
The dumped OIDs are as follows:
$VAR1 = bless( {
'oid' => '52faf6de10d041d196cb545b'
}, 'Mango::BSON::ObjectID' );
$VAR2 = bless( {
'oid' => '5300409310d041d196cb545d'
}, 'Mango::BSON::ObjectID' );
and the found objects will look something like this (depending on your structure):
$VAR1 = [
{
'_id' => bless( {
'oid' => '52faf6de10d041d196cb545b'
}, 'Mango::BSON::ObjectID' ),
'class' => [
'Sport'
],
'make' => '4321',
'year' => 2012
},
{
'_id' => bless( {
'oid' => '5300409310d041d196cb545d'
}, 'Mango::BSON::ObjectID' ),
'class' => [
'Classic'
],
'make' => '1234',
'year' => 2014
}
]
You seem to be looking for the $in operator. So rather than looping all the items to find the ones that match you pass in the _id values sent to your controller within the call to find():
my #oids = $self->req->params->param('ids[]');
$self->mango->db->collection('items')
->find({ "_id" => { '$in' => \#oids } })->all(sub {
my ($collection, $err, $items) = #_;
return $self->render_exception($err) if $err;
$self->render(json => {items => $items});
});
Update
Actually had some time to test this with Mango and managed to get a working case:
{ "_id" : ObjectId("5345faf8a1f97e61848485e8"), "prerequisites" : [ "a" ] }
{ "_id" : ObjectId("5345fafea1f97e61848485e9"), "prerequisites" : [ "a", "b" ] }
{ "_id" : ObjectId("5345fb02a1f97e61848485ea"), "prerequisites" : [ "a", "c" ] }
And then running with the code:
#!/usr/bin/env perl
use Modern::Perl;
use EV;
use AnyEvent;
use Data::Dumper;
use Mango;
use Mango::BSON::ObjectID;
my $mango = Mango->new();
my $cv = AE::cv;
my $col = $mango->db('test')->collection('courses');
my #oids = (
"5345fafea1f97e61848485e9",
"5345fb02a1f97e61848485ea"
);
#oids = map { Mango::BSON::ObjectID->new($_) } #oids;
$col->find({ '_id' => { '$in' => \#oids } })->all( sub {
my ( $cursor, $err, $docs ) = #_;
$cv->send( Dumper( $docs ) );
});
say $cv->recv;
So that actually will select the correct documents from the sample, and it does seem that the ObjectID values need to be passed in that way in order to match.
At least there should be enough there to debug from.

How do I sort hash of hashes by a sub key/value using perl?

I would like sort the following Hash. parentXX should be sort with the value of __displayorder, the Xtopic of a parent and Xprod of a topic should sort alphabeticly.
$VAR1 = {
'views' => {
'parent23' => {
'__displayorder' => 2,
'vtopic1' => {
'gprod1.1' => undef,
'aprod1.2' => undef,
},
'btopic2' => {
'tprod2.1' => undef,
'mprod2.2' => undef,
},
},
'parent98' => {
'__displayorder' => 1,
'atopic1' => {
'qprod1.1' => undef,
'jprod1.2' => undef,
},
'xtopic2' => {
'kprod2.1' => undef,
'fprod2.2' => undef,
}
}
}
}
You can't sort a hash. Can you make do with having the names of the views in the specific order?
my $views = $VAR1->{views};
my #sorted_view_keys = sort {
$views->{$a}{__displayorder} cmp $views->{$b}{__displayorder}
} keys(%$views);
Or maybe you want the sorted views?
my #sorted_views = map { $views->{$_} } #sorted_view_keys;
-or-
my #sorted_views = #$views[#sorted_view_keys];
As already mentioned, you can't sort a normal Perl hash. Perl hashes are unordered. But you can use the CPAN module Tie::IxHash to get an ordered hash. The lines below transform all the sub-hashes from your sample output into Tie::IxHash hashes and do some sorting (e.g. alphabetically or by display order):
use Tie::IxHash;
my $views = $VAR1->{views};
while(my($view_key, $view) = each %$views) {
while(my($topic, $prods) = each %$view) {
next if $topic =~ m{^__};
tie my %new_prods, 'Tie::IxHash', (map { ($_ => $prods->{$_}) } sort keys %$prods);
$view->{$topic} = \%new_prods;
}
tie my %new_view, 'Tie::IxHash', (map { ($_ => $view->{$_}) } sort keys %$view);
$views->{$view_key} = \%new_view;
}
tie my %new_views, 'Tie::IxHash', (map { ($_ => $views->{$_}) } sort { $views->{$a}->{__displayorder} <=> $views->{$b}->{__displayorder} } keys %$views);
$VAR1->{views} = \%new_views;

Perl adding Lines into a Multi-Dimensional Hash

Hello I want to split a Line and add the Values in to a multi dimensional Hash. This is how the Lines look like:
__DATA__
49839382;Test1;bgsae;npvxs
49839384;Test2;bgsae;npvxs
49839387;Test3;bgsae;npvxs
So what I am doing now is:
my %prefix = map { chomp; split ';' } <DATA>;
But now I can only access Test1 with:
print $prefix{"49839382"}
But how can I also add the bgsae to the Hash so I can access is with
$prefix{"49839382"}{"Test1"}
Thank you for your help.
What structure are you trying to build?
use Data::Dumper;
my %prefix = map { chomp (my #fields = split /;/); $fields[0] => { #fields[1 .. $#fields] } } <DATA>;
print Dumper \%prefix;
Output:
$VAR1 = {
'49839384' => {
'Test2' => 'bgsae',
'npvxs' => undef
},
'49839382' => {
'Test1' => 'bgsae',
'npvxs' => undef
},
'49839387' => {
'npvxs' => undef,
'Test3' => 'bgsae'
}
};
Or do you need a deeper hash?
my %prefix;
for (<DATA>) {
chomp;
my $ref = \%prefix;
for (split /;/) {
warn "[$_]";
$ref->{$_} = {};
$ref = $ref->{$_};
}
}
Returns:
$VAR1 = {
'49839384' => {
'Test2' => {
'bgsae' => {
'npvxs' => {}
}
}
},
'49839382' => {
'Test1' => {
'bgsae' => {
'npvxs' => {}
}
}
},
'49839387' => {
'Test3' => {
'bgsae' => {
'npvxs' => {}
}
}
}
};
I don't know what you need the data for, but at a guess you want something more like this.
It builds a hash of arrays, using the first field as the key for the data, and the remaining three in an array for the value. So you can access the test number as $data{'49839382'}[0] etc.
use strict;
use warnings;
my %data = map {
chomp;
my #fields = split /;/;
shift #fields => \#fields;
} <DATA>;
use Data::Dumper;
print Data::Dumper->Dump([\%data], ['*data']);
__DATA__
49839382;Test1;bgsae;npvxs
49839384;Test2;bgsae;npvxs
49839387;Test3;bgsae;npvxs
output
%data = (
'49839384' => [
'Test2',
'bgsae',
'npvxs'
],
'49839382' => [
'Test1',
'bgsae',
'npvxs'
],
'49839387' => [
'Test3',
'bgsae',
'npvxs'
]
);

How do I create and add anonymous hashes to a known Hash during script execution?

I'll attempt to illustrate this with an example. Take a common example of a Hash of Hashes:
my %HoH = (
flintstones => {
lead => "fred",
pal => "barney",
},
jetsons => {
lead => "george",
wife => "jane",
"his boy" => "elroy",
},
simpsons => {
lead => "homer",
wife => "marge",
kid => "bart",
},
);
For my purposes, I would like to be able to add an unnamed, or anonymous hashes to %HOH. I won't need (or be able to) define these sub-hashes until runtime. How can I accomplish this with Perl?
Everything I've read (and I have read through Perldocs and Google'd already) seems to show examples where all sub-hahes (e.g. "flintstones", "jetsons", and "simpsons") are defined.
What I am doing is attempting to build a parent Hash that will contain sub-hashes with rows from a CSV file:
%TopHash = (
%Line1 => {
cell01 => $some_value1a;
cell02 => $some_value2a;
cell03 => $some_value3a;
},
%Line2 => {
cell01 => $some_value1b;
cell02 => $some_value2b;
cell03 => $some_value3b;
},
%Line3 => {
cell01 => $some_value1c;
cell02 => $some_value2c;
cell03 => $some_value3c;
},
# etc
# etc
# etc
);
The number of "%LineX" hashes that I need is not known until runtime (because they represent the number of lines in a CSV that is read at runtime).
Any ideas? If it isn't clear already...I still am trying to wrap my head around Perl hashes.
To add an anonymous hash at runtime, assign it as you would a normal hash element:
$HoH{key} = { foo => 42 };
or
$HoH{key} = $hash_ref;
or
$HoH{key} = \%hash;
First you create the hash from the current line you're parsing
my %lineHash = (
cell01 => $some_value1a,
cell02 => $some_value1b,
cell03 => $some_value1c
);
or create a reference to a hash outright
my $lineHashRef = {
cell01 => $some_value2a,
cell02 => $some_value2b,
cell03 => $some_value2c
};
Then you add it to your overall hash, remembering that nested perl structures just contain references to the other structures.
$topHash{line1} = \%lineHash;
$topHash{line2} = $lineHashRef;
Updated
Example given a loop over an array of data to parse
my %topHash;
foreach my $i (0 .. $#data) {
my %tempHash;
// stuff here to parse $data[$i] and populate %tempHash
$topHash{"line$i"} = \%tempHash;
}
#!/usr/bin/perl
use strict;
my %HoH = (
line01 => {
cell01 => "cell0101",
cell02 => "cell0102",
cell03 => "cell0103"
}
);
$HoH{"line02"} =
{
cell01 => "cell0201",
cell02 => "cell0202",
cell03 => "cell0203"
};
foreach my $hohKey (keys %HoH)
{
my $newHash = $HoH{$hohKey};
print "Line Name: $hohKey\n";
foreach my $key (keys %$newHash)
{
print "\t$key => ", $newHash->{$key}, "\n";
}
}
Everytime you create a new hash from a line of data, you'll need to think of a unique key to store that data in your top hash table.
my $line = 1;
my %HoH;
while (<>) {
my ($cell01, $cell02, $cell03, #etc) = split /,/;
my $newHash = { cell01 => $cell01, cell02 => $cell02, ... };
my $key = "line$line";
$HoH{$key} = $newHash;
$line++;
}
Now keys(%HoH) will return a (unsorted) list like "line1","line2","line3",....
$HoH{"line5"} would return a reference to the data for the 5th line of your file.
%{$HoH{"line7"}} is kind of ugly syntax but it returns a hashtable of your data
from line 7.
$HoH{"line14"}{"cell02"} could be used to get at a specific piece of data.