Convert array of arrays to array of hash - arrays

I have following array of arrays in Perl that are getting as multiple rows in database.
$arrayref = [
[ 1, "name1", "name2" ],
[ 2, "name3", undef ],
[ 3, "name5", "name6" ],
[ 4, "name10", undef ],
];
I want to make this as an array of hashes like this
my #array = (
{ id => 1, name => "name1", l_name => "name2" },
{ id => 2, name => "name3", l_name => undef },
{ id => 3, name => "name5", l_name => "name6" },
{ id => 4, name => "name10", l_name => undef },
);

You can use map {} to transform array references to hash references,
my #cols = qw(id name l_name);
my #array = map { my %h; #h{#cols} = #$_; \%h } #$arrayref;
or
use List::MoreUtils qw( zip );
my #cols = qw(id name l_name);
my #array = zip(\#cols, #$arrayref);

I have following array of arrays in Perl that are getting as multiple rows in database
You are presumably calling
$sth->fetchall_arrayref();
If instead you use an empty anonymous hash as the first parameter
$sth->fetchall_arrayref( {} );
then DBI will return the data in the format you want as an array of hashes
The DBI documentation describes it here
If $slice is a hash reference, fetchall_arrayref fetches each row as a hash reference. If the $slice hash is empty then the keys in the hashes have whatever name lettercase is returned by default. (See FetchHashKeyName attribute.) If the $slice hash is not empty, then it is used as a slice to select individual columns by name. The values of the hash should be set to 1. The key names of the returned hashes match the letter case of the names in the parameter hash, regardless of the FetchHashKeyName attribute.
For example, to fetch all fields of every row as a hash ref:
$tbl_ary_ref = $sth->fetchall_arrayref({});

Related

How do I append a new hash to an array of hashes?

If I wanted to add a new hash to all the arrays in the mother_hash using a loop, what would be the syntax?
My hash:
my %mother_hash = (
'daughter_hash1' => [
{
'e' => '-4.3',
'seq' => 'AGGCACC',
'end' => '97',
'start' => '81'
}
],
'daughter_hash2' => [
{
'e' => '-4.4',
'seq' => 'CAGT',
'end' => '17',
'start' => '6'
},
{
'e' => '-4.1',
'seq' => 'GTT',
'end' => '51',
'start' => '26'
},
{
'e' => '-4.1',
'seq' => 'TTG',
'end' => '53',
'start' => '28'
}
],
#...
);
If you have a hash of arrays of hashes and want to add a new hash to
the end of each of the arrays, you can do:
push #{ $_ }, \%new_hash for (values %mother_hash);
This loop iterates over the values of %mother_hash (which are array refs in this case) and setting $_ for each iteration. Then in each iteration, we push the reference to the new hash %new_hash to the end of that array.
First I would point out the daughter hashes aren't hashes but arrays of anonymous hashes. To add another daughter hash:
$mother_hash{daughter_hash3} = [ { %daughter_hash3 } ];
This creates an anonymous array that contains an anonymous hash with the contents of %daughter_hash3.
For a loop:
$mother_hash{$daughter_hash_key} = [ { %daughter_hash } ];
where $daughter_hash_key is a string contain the key for the %mother_hash and %daughter_hash is the hash to add.
To add another hash to a daughter array with key $daughter_hash_key:
push #{ $mother_hash{$daughter_hash_key} }, { %daughter_hash };
I know ti's complicated but I suggest you use Data::Dumper to dump the contents of %mother_hash each time thru the loop to see if it grows correctly.
use Data::Dumper;
print Dumper \%mother_hash;
See perldoc Data::Dumper for details..
Data::Dumper is a standard module that comes with Perl. For a list of standard modules, see perldoc perlmodlib.
mother_hash is a hash of arrays of hashes.
To add another top-level array of hashes.
%mother_hash{$key} = [ { stuff }, { stuff } ];
To add another entry to an existing array
push #{%mother_hash{'key'}} { stuff };
To add another entry to the hash in the embedded array
%{#{%mother_hash{'top_key'}}[3]}{'new_inner_key'} = value;
When confused and attempting to match up the "types" of hash / array / scalar containing a hash reference / array reference, you can use the following technique
use Data::Dumper;
$Data::Dumper::Terse = 1;
printf("mother_hash reference = %s\n", Dumper(\%mother_hash));
printf("mother_hash of key 'top_key' = %s\n", Dumper(%mother_hash{top_key}));
and so on to find your way through a large data structure and validate that you are narrowing down to the region you want to access or alter.

Looping through an array, displaying elements that match a criteria

I have this big array that I need to break down and only display specific elements within it that match a criteria.
My array looks like this.
[
{
:id => 9789,
:name => "amazing location",
:priority => 1,
:address_id => 12697,
:disabled => false
},
{
:id => 9790,
:name => "better location",
:priority => 1,
:address_id => 12698,
:disabled => false
},
{
:id => 9791,
:name => "ok location",
:priority => 1,
:address_id => 12699,
:disabled => true
}
]
What I need is to only display the elements within this array that have disabled set to true.
However when I try this, I get the error stating no implicit conversion of Symbol into Integer
array.map do |settings, value|
p hash[:disabled][:true]
end
I'm wondering if there is another way, or if there is a way to do this. If anyone could take a look, I would greatly appreciate it.
By providing two arguments to #map on an array, you're actually getting the first hash and then nil. When in reality you just want to loop for each and select those where disabled is true. You can do that instead with Array#select which will filter all elements of the array where the block returns a truthy value:
print array.select { |hash| hash[:disabled] }
=> [{:id=>9791, :name=>"ok location", :priority=>1, :address_id=>12699, :disabled=>true}]
You can try this with a short each or select.
a.each { |k,_v| puts k if k[:disabled] == true }
=> {:id=>9791, :name=>"ok location", :priority=>1, :address_id=>12699, :disabled=>true}
This iterates over each element (hash) inside the array you have and checks if the value of the key disabled on each value is true, and puts the key, just for example, you can set it as you want to do.
Or shorter:
puts a.select { |k,_v| k[:disabled] }
=> {:id=>9791, :name=>"ok location", :priority=>1, :address_id=>12699, :disabled=>true}
Your error shows up when you are treating an array or string as a Hash.
In PHP, array keys can be either numbers or strings, whereas in Ruby associative arrays are a separate data type, called a hash.
Here’s a cheatsheet for various foreach variants, translated into idiomatic Ruby:
Looping over a numeric array (PHP) :
<?php
$items = array( 'orange', 'pear', 'banana' );
# without indexes
foreach ( $items as $item ) {
echo $item;
}
# with indexes
foreach ( $items as $i => $item ) {
echo $i, $item;
}
Looping over an array (Ruby) :
items = ['orange', 'pear', 'banana']
# without indexes
items.each do |item|
puts item
end
# with indexes
items.each_with_index do |item, i|
puts i, item
end
Looping over an associative array (PHP) :
<?php
$continents = array(
'africa' => 'Africa',
'europe' => 'Europe',
'north-america' => 'North America'
);
# without keys
foreach ( $continents as $continent ) {
echo $continent;
}
# with keys
foreach ( $continents as $slug => $title ) {
echo $slug, $title;
}
Looping over a hash (Ruby):
continents = {
'africa' => 'Africa',
'europe' => 'Europe',
'north-america' => 'North America'
}
# without keys
continents.each_value do |continent|
puts continent
end
# with keys
continents.each do |slug, title|
puts slug, title
end
In Ruby 1.9 hashes were improved so that they preserved their internal order. In Ruby 1.8, the order in which you inserted items into a hash would have no correlation to the order in which they were stored, and when you iterated over a hash, the results could appear totally random. Now hashes preserve the order of insertion, which is clearly useful when you are using them for keyword arguments in method definitions. (thanks steenslag for correcting me on this)

Modify array of hashes by reference

I want to modify an array of hashes by a sub function, therefore I want to handover the array by reference, de-reference in the sub function and modify it furthermore.
After this modification, the array shall hold the modified values instantly, I don't want to explicitely return the modified hash (want to work on the original array).
Unfortunately I do not succeed with that. There are many web hints concerning access to references of array of hashes, but I couldn't find one which manipulates the array.
my #array_of_hashes = ( {name => "Alice"},
{name => "Bob"} );
my $myhashref = \%{$array_of_hashes[0]}; # This holds a ref to {name=>"Alice"}
my %myhash = %{$myhashref}; # De-reference, shall be the Hash to work on
print $myhash{name} . "\n"; # This shows Alice
$myhash{age}=32; # Want to add 'age' to the Alice Hash, does not work
This modified hash does not show {age}. When you have a look at #array_of_hashes with print Data::Dump::dump(#array_of_hashes) the line $myhash{age}=32; has no impact on #array_of_hashes.
How can I hand over a reference to e.g. the first element of #array_of_hashes to a function and how to I have to dereference it in the function in order to be able to modify the hash within #array_of_hashes?
You said: I want to modify an array of hashes by a sub function
If I understand right, something like the following could work:
use 5.014;
use warnings;
use Data::Dumper;
my #aoh = (
{name => "Alice"},
{name => "Bob"}
);
do_some(\#aoh); #pass arrayref
$aoh[1]->{text} = 'huhu';
say Dumper \#aoh;
say "$aoh[1]->{name} has age $aoh[1]->{age} and says $aoh[1]->{text}";
sub do_some {
my $ar = shift;
for my $hr (#$ar) { #for each array element
$hr->{age} = int rand 100;
}
}
# however (IMHO)
# using arrayref from the beginning is more cleaner
my $aohr = [
{name => "Alice"},
{name => "Bob"}
];
do_some($aohr);
$aohr->[0]->{text} = 'juju';
say Dumper $aohr;
say "$aohr->[0]->{name} has age $aohr->[0]->{age} and says $aohr->[0]->{text}";
#could use the shortened form
#say "$aohr->[0]{name} has age $aohr->[0]{age} and says $aohr->[0]{text}";
the above produces for example:
$VAR1 = [
{
'age' => 31,
'name' => 'Alice'
},
{
'age' => 10,
'text' => 'huhu',
'name' => 'Bob'
}
];
Bob has age 10 and says huhu
$VAR1 = [
{
'name' => 'Alice',
'age' => 94,
'text' => 'juju'
},
{
'name' => 'Bob',
'age' => 57
}
];
Alice has age 94 and says juju
You already created a ref to each hash when you create the array. then you defrencing the hash and allocating it to a new hash varible.
my %myhash = %{$myhashref}; # De-reference, shall be the Hash to work on
So you now have a new hash which was created as a copy of the alice hash. However the new hash and the alice hash are seperate. you then modify the new hash works ok but it will not be reflected in the alice hash as they are seperate. instead you should modify the existing hash ref. For example try below.
use strict;
use warnings;
use Data::Dumper;
my #array_of_hashes = ( {name => "Alice"},
{name => "Bob"} );
print $array_of_hashes[0]->{'name'}, "\n";#this shows alice
$array_of_hashes[0]->{'age'}=32; # Want to add 'age' to the Alice Hash, does not work
print Dumper \#array_of_hashes;

Sorting Hash of Hashes by value

I have the following data structure
my %HoH = {
'foo1' => {
'bam' => 1,
'zip' => 0,
},
'foo2' => {
'bam' => 0,
'zip' => 1,
'boo' => 1
}
};
I would like to sort KEY1 (foo1 or foo2) by the VALUE stored in 'zip' in order from greatest to least.
Here's how I'm doing it.
use strict; use warnings;
use Data::Dumper;
my #sorted;
foreach my $KEY1 (keys %HoH) {
# sort KEY1 by the value 'zip' maps to in descending order
#sorted = sort {$HoH{$KEY1}{'zip'}{$b} <=>
$HoH{$KEY1}{'zip'}{$a}} keys %HoH;
}
print Dumper(\#sorted);
I'm getting an weird warning: Reference found where even-sized list expected at test.pl line 6.
Also print Dumper(\#sorted); is printing
$VAR1 = [
'HASH(0x1b542a8)'
];
When it should be printing
$VAR1 = [
['foo2', 'foo1']
];
Since foo2 has 1 zip and foo1 has 0 zip.
%HoH is declared as a hash, but is defined as a hashreference. Use parentheses (...) instead of braces {...}.
You don't need to loop through the hash to sort it. Sort will take care of that.
if you sort {...} keys %HoH, then the special variables $a and $b represent the keys of %HoH as it performs the sort.
$a and $b are in reverse order because your expected result is in decreasing order. (Update: Oh I just noticed that you had that in the first place.)
The zip value in the nested hash is $HoH{$KEY}{'zip'}, which is what you should sort by.
use strict;
use warnings;
use Data::Dumper;
my %HoH = (
'foo1' => {
'bam' => 1,
'zip' => 0,
},
'foo2' => {
'bam' => 0,
'zip' => 1,
'boo' => 1
}
);
my #sorted = sort {$HoH{$b}{'zip'} <=> $HoH{$a}{'zip'}} keys %HoH;
print Dumper \#sorted;
Note that the result of this code will give you an array:
$VAR1 = [
'foo2',
'foo1'
];
... not a nested array:
$VAR1 = [
['foo2', 'foo1']
];

Array in value of hash perl

Is it possible to assign the reference of an array as the value in the key : value pair of a hash table in perl?
Yes it is. Create a reference to the array by using backslash:
$hash{key} = \#array;
Note that this will link to the actual array, so if you perform a change such as:
$array[0] = "foo";
That will also mean that $hash{key}[0] is set to "foo".
If that is not what you want, you may copy the values by using an anonymous array reference [ ... ]:
$hash{key} = [ #array ];
Moreover, you don't have to go through the array in order to do this. You can simply assign directly:
$hash{key} = [ qw(foo bar baz) ];
Read more about making references in perldoc perlref
Yes. See http://perlmonks.org/?node=References+quick+reference for some basic rules for accessing such data structures, but to create it, just do one of these:
%hash = ( 'somekey' => \#arrayvalue );
$hash{'somekey'} = \#arrayvalue;
%hash = ( 'somekey' => [ ... ] );
use Data::Dumper; #name=('5/17',
'5/17','5/17','5/17','5/17','5/17','5/17','5/17'); #status_flags=('U
H L','U C','U H L','U C','U C','U H L','U C', 'U H L');
#ip_address=('192.168.0.11','192.168.0.2','192.168.0.13','192.168.0.0','192.168.0.3','192.168.0.12','192.168.0.4','192.168.0.14'); #dp_id=('0','0','0','0','0','0','0','0');
#ip_prefix_length=('32','32','32','24', '32', '32','32','32');
for ($value=0;$value<=5;$value++) {
$keyvals{'Response'}{'brocade-extension-ip-route'}{'extension-ip-route'}={'name'=>"$name[$value]"};
$keyvals{'Response'}{'brocade-extension-ip-route'}{'extension-ip-route'}={'dp-id'=>"$dp_id[$value]"};
$keyvals{'Response'}{'brocade-extension-ip-route'}{'extension-ip-route'}={'ip-address'=>"$ip_address[$value]"};
$keyvals{'Response'}{'brocade-extension-ip-route'}{'extension-ip-route'}={'ip-prefix-length'=>"$ip_prefix_length[$value]"};
$keyvals{'Response'}{'brocade-extension-ip-route'}{'extension-ip-route'}={'ip-gateway'=>'*'};
}
print Dumper \%keyvals;
Each array value assign into hash value. $var1= {
'Response' => {
'extension-ip-route' => {
'status-flags' => 'U H L '
,
'ip-gateway' => '*',
'name' => '0/2',
'ip-address' => '192.168.20.11',
'dp-id' => '0',
'ip-prefix-length'=>'32'
}
}
};

Resources