Copy array to hash - perl

I'm trying to copy an array to a hash, such that each element of the array is a key, followed by an empty value.
my %questions = map { #u_list => $_ } #u_list;
This only prints out
=>
I see on perldoc this idiom:
%hash = map { get_a_key_for($_) => $_ } #array;
But I cannot figure out how to set the keys. I want the keys to be each element in the array.

Super confusing but functional answer:
#questions{#u_list}=();
This is using the hash slice syntax to specify a set of hash keys..

my %questions = map { $_ => undef } #u_list;
In the map, each element of #u_list gets set to $_.

%hash = map { $_ => '' } #array;
This sets the values to an empty string

$_ is the current element of your list #u_list.
So you have to say
my %questions = map { $_ => 1 } #u_list;
to map your list elements as hash keys.

Here are a few different ways to do this, just for reference.
Using map
my %questions = map { $_, undef } #u_list;
Using a foreach
my %questions;
$questions{$_} = undef foreach ( #u_list );
Using a hash slice.
my %questions;
#questions{#u_list} = (undef) x #u_list;

Related

Perl pass hash reference to Subroutine from Foreach loop (Array of Hash)

This may be something very simple for you but i have been trying for it for more than one hour.
Ok.. Here is my code,
#collection = [
{
'name' => 'Flo',
'model' => '23423',
'type' => 'Associate',
'id' => '1-23928-2392',
'age' => '23',
},
{
'name' => 'Flo1',
'model' => '23424',
'type' => 'Associate2',
'id' => '1-23928-23922',
'age' => '25',
}];
foreach my $row (#collection) {
$build_status = $self->build_config($row);
}
sub build_config {
my $self = shift;
my %collect_rows = #_;
#my %collect_rows = shift;
print Dumper %collect_rows; exit;
exit;
}
Where $row is really an Hash. But when i Print it.. It gives output like below (I am using Dumper),
$VAR1 = 'HASH(0x20a1d68)';
You are confusing references with hashes and arrays.
An array is declared with:
my #array = (1, 2, 3);
A hash is declared with:
my %hash = (1 => 2, 3 => 4);
That's because both hashes and arrays are simply lists (fancy lists, but I digress). The only time you need to use the [] and {} is when you want to use the values contained in the list, or you want to create a reference of either list (more below).
Note that the => is just a special (ie. fat) comma, that quotes the left-hand side, so although they do the same thing, %h = (a, 1) would break, %h = ("a", 1) works fine, and %h = (a => 1) also works fine because the a gets quoted.
An array reference is declared as such:
my $aref = [1, 2, 3];
...note that you need to put the array reference into a scalar. If you don't and do it like this:
my #array = [1, 2, 3];
... the reference is pushed onto the first element of #array, which is probably not what you want.
A hash reference is declared like this:
my $href = {a => 1, b => 2};
The only time [] is used on an array (not an aref) is when you're using it to use an element: $array[1];. Likewise with hashes, unless it's a reference, you only use {} to get at a key's value: $hash{a}.
Now, to fix your problem, you can keep using the references with these changes:
use warnings;
use strict;
use Data::Dumper;
# declare an array ref with a list of hrefs
my $collection = [
{
'name' => 'Flo',
...
},
{
'name' => 'Flo1',
...
}
];
# dereference $collection with the "circumfix" operator, #{}
# ...note that $row will always be an href (see bottom of post)
foreach my $row (#{ $collection }) {
my $build_status = build_config($row);
print Dumper $build_status;
}
sub build_config {
# shift, as we're only accepting a single param...
# the $row href
my $collect_rows = shift;
return $collect_rows;
}
...or change it up to use non-refs:
my #collection = (
{
'name' => 'Flo',
...
},
{
'name' => 'Flo1',
...
}
);
foreach my $row (#collection) {
my $build_status = build_config($row);
# build_config() accepts a single href ($row)
# and in this case, returns an href as well
print Dumper $build_status;
}
sub build_config {
# we've been passed in a single href ($row)
my $row = shift;
# return an href as a scalar
return $row;
}
I wrote a tutorial on Perl references you may be interested in guide to Perl references. There's also perlreftut.
One last point... the hashes are declared with {} inside of the array because they are indeed hash references. With multi-dimentional data, only the top level of the structure in Perl can contain multiple values. Everything else underneath must be a single scalar value. Therefore, you can have an array of hashes, but technically and literally, it's an array containing scalars where their value is a pointer (reference) to another structure. The pointer/reference is a single value.

access hash with hashes

I have this hash with hashses.
I would like to iterate only the value of '0'.
$VAR1 = {
'1' => {
'192.168.1.1' => '192.168.1.38'
},
'0' => {
'192.168.32.6' => '192.168.32.43'
}
};
The only way I can access it is by creating two foreach my $key (keys(%myhash)) loops:
Can I use:
foreach my $key (keys(%myhash{0})) ## does not work
or directly access the values somehow?
Thanks
First of all, if you are using consecutive integers as keys to a hash then it is likely that you should be using an array instead.
The value of the hash corresponding to key 0 is $dhcpoffers{0} because it is a scalar value. %dhcpoffers{0} is just a syntax error.
You need
for my $key (keys %{ $dhcpoffers{0} }) { ... }
or, if you prefer
my $offer_0 = $dhcpoffers{0};
for my $key (keys %$offer_0) { ... }
Since version 14 of Perl 5, keys will accept a hash reference, so you may be able to write the much cleaner
for my $key (keys $dhcpoffers{0}) { ... }

Delete an array element completely

Goal: Remove a particular Value from an array
I have written a script and it works fine but I am not happy the way I have written it. So I am curious to know is there a better way to write it. please consider below use case:
I have a nested hash/hash/array...like below. I need to remove any array values which has local in their name:
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my $hash = { esx1 =>
{ cluster => "clu1",
fd => "fd1",
ds => [
'ds1',
'ds2',
'localds',
],
},
esx2 =>
{ cluster => "clu2",
fd => "fd2",
ds => [
'ds3',
'ds4',
'dslocal',
],
},
};
foreach my $a ( keys %$hash )
{
foreach ( 0..$#{ $hash->{$a}->{ds} } )
{
delete $hash->{$a}->{ds}->[$_] if $hash->{$a}->{ds}->[$_] =~ /local/i;
#{ $hash->{$a}->{ds} } = grep defined, #{ $hash->{$a}->{ds} };
}
}
print Dumper ($hash);
so the script deletes the "localds" and "dslocal" and keeps everything else intact.
Question:
Is there a cleaner way to write the foreach ( 0..$#{$hash->{$a}->{ds} } ) loop
If I do not write the grep line above, the resultant array has the value containing local deleted but is replaced by undef. Why is this happening.
Thanks.
delete is for elements in hashes. It happens to work on arrays by a quirk of implementation, but it shouldn't be relied on.
For arrays, you want to use splice.
splice #{ $ref->{to}->{array} }, $index, 1, ();
This replaces the 1-element sublist starting at $index with (), the empty list.
Why first iterate through array and delete elements and then look for "not-deleted" nodes (side note - this grep should be outside loop)? You can look for good nodes from very start! Replace entire loop with:
foreach my $a ( keys %$hash )
{
#{ $hash->{$a}->{ds} } = grep { !/local/i } #{ $hash->{$a}->{ds} };
}
for my $h (values %$hash){
$h->{ds} = [ grep { $_ !~ /local/i } #{$h->{ds}} ];
}
Not much neater but:
foreach my $a ( keys %$hash )
{
my $temp;
foreach ( #{ $hash->{$a}->{ds} } )
{
push(#$temp, $_) unless $_ =~ /local/i;
}
$hash->{$a}->{ds} = $temp;
}
Delete doesn't alter the array structure, it just alters the array content. Because of this in your method you need to grep for defined entries to create a new array of the structure you desire, then overwrite the old array.
edit:
This is better explained on perldoc page for delete
delete() may also be used on arrays and array slices, but its behavior is less straightforward. Although exists() will return false for deleted entries, deleting array elements never changes indices of existing values; use shift() or splice() for that. However, if all deleted elements fall at the end of an array, the array's size shrinks to the position of the highest element that still tests true for exists(), or to 0 if none do.
edit:
As pointed out by mob splice will do what you want:
foreach my $a ( keys %$hash )
{
for(0..$#{ $hash->{$a}->{ds} } )
{
splice( #{ $hash->{$a}->{ds} }, $_, 1 ) if $hash->{$a}->{ds}->[ $_ ] =~ /local/i;
}
}
You could use List::MoreUtils qw/first_idx/ to get the index of /local/ like so
first_idx { $_ =~ /local/i } #{$hash->{$a}->{ds}};
Then do what you want with that.

Perl map block local variable usage

This code compiles a set by way of hash keys of the unique basename stubs in a set of paths.
%stubs = map { $f=basename $_; $f =~ /^([A-Za-z]+[0-9]+)\./ ; $1=>() } #pathlist;
Why do I need the $f references here? I thought I'd be ok with:
%stubs = map { basename; /^([A-Za-z]+[0-9]+)\./; $1=>() } #pathlist;
But I get no match. Am I not permitted to modify $_ in the map block?
For those wondering what the code is doing:
For each $path (#pathlist), it's getting the basename, matching the first letter-number sequence, and then returning the first bracket match as the key on an empty list value. Example:
/some/dir/foo123.adfjijoijb
/some/dir/foo123.oibhobihe
/some/dir/bar789.popjpoj
returns
foo123 => ()
bar789 => ()
After which I use the keys of the map as the set of values so process.
basename does not default to acting on $_. But you can match against its return value instead of using $f:
%stubs = map { basename($_) =~ /^([A-Za-z]+[0-9]+)\./; $1 => undef } #pathlist;
Note that () in a list doesn't produce an element, it just flattens to nothing; you have to provide a value, even if only undef. With $1 => (), map iterations would alternate producing a key and a value for %stubs.
It's good to always check that your regex succeed before using $1:
%stubs = map { basename($_) =~ /^([A-Za-z]+[0-9]+)\./ ? ($1 => undef) : () } #pathlist;
though if you don't mind the hash values being the empty string instead of undef, you can just make the regex match return the desired list:
%stubs = map { basename($_) =~ /^([A-Za-z]+[0-9]+)()\./ } #pathlist;
In map and grep, $_ is an alias for the values in the array. If you modify them, you actually modify the values in the array. This is probably not what you want and probably what is going wrong, but to debug print keys %stubs and #pathlist afterwards in both cases and let us know what it says.
Also: File::Basename's basename does not implicitly work on $_. It generates an error for me.
#!/usr/bin/perl
use feature say;
use File::Basename;
#pathlist=("/some/dir/foo123.adfjijoijb","/some/dir/foo123.oibhobihe","/some/dir/bar789.popjpoj");
%stubs1 = map { $f=basename $_; $f =~ /^([A-Za-z]+[0-9]+)\./ ; $1=>() } #pathlist;
say join(',',keys %stubs1);
say "---";
say join(',',#pathlist);
say "---";
%stubs = map { $_=basename $_; /^([A-Za-z]+[0-9]+)\./; $1=>() } #pathlist;
say join(',',keys %stubs);
say "---";
say join(',',#pathlist);
say "---";
%stubs = map {basename; /^([A-Za-z]+[0-9]+)\./; $1=>() } #pathlist;
Alternate implementation:
my %stubs =
map { $_ => undef }
map { basename($_) =~ /^([A-Za-z]+[0-9]+)\./ }
#pathlist;

How can I elegantly create a hash from an array reference in Perl?

I am looking for a more elegant way to create a hash that contains the list I have read in from my configuration file. Here is my code:
read_config($config_file => my %config);
my $extension_list_reference = $config{extensions}{ext};
my #ext;
# Store each item of the list into an array
for my $i ( 0 .. (#$extension_list_reference - 1) ) {
$ext[$i] = $extension_list_reference->[$i];
}
# Create hash with the array elements as the keys
foreach my $entry (#ext) {
$extensions{$entry} = "include";
}
Thanks.
my %hash = map { $_ => 'include' } #list;
Try using map:
http://perldoc.perl.org/functions/map.html
Here's what your new code should look like:
my %extensions = map { $_ => "include" } #{ $config{extensions}{ext} };
If I understand your problem, this is how you do it in one line:
#extensions{#$extension_list_reference} = ();
Note: each value of the hash is empty, but you still can check whether the key exists in the hash using function exists, like this:
if(exists $extensions{$some_key}) {...
P.S. If by some reason you really need those strings 'include' as values, you can have them, too:
#extensions{#$extension_list_reference} = ('include') x #$extension_list_reference;
This way:
read_config($config_file => my %config);
%extensions = map +($_ => "include"), #{$config{extensions}{ext}};
or this way:
read_config($config_file => my %config);
#extensions{#{$config{extensions}{ext}}} = ("include") x #{$config{extensions}{ext}};