Grep not returning expected value Perl - arrays

I'm trying to grep a value in Perl:
#array = ['hello', 'world'];
if (grep(/hello/i, #array) {
# do something
}
For some reason my code isn't picking this up. Perhaps there's another way I can do this.
the Array itself is inside a hash:
hash => {
array => ['hello', 'world'],
value => 'feature',
}

You're building your array wrong. This line creates a one-element array, and that element is an array reference.
#array = ['hello', 'world'];
When you grep over that one-element array, that one array reference doesn't match /hello/i.
What you want is:
#array = ('hello', 'world');

After
#array = ['hello', 'world'];
you have:
$ perl -MData::Dumper -e '#array = ['hello', 'world']; print Dumper \#array'
$VAR1 = [
[
'hello',
'world'
]
];
That is #array contains a reference to an anonymous array containing the strings 'hello' and 'world'.
Then, in grep, you evaluate this reference as a string. Therefore, your grep does a single comparison along the lines of
'ARRAY(0x7fa0e38032b8)' =~ /hello/i;
Clearly, that is not going to match.
#!/usr/bin/env perl
use strict;
use warnings;
my %hash = (array => ['hello', 'world']);
if (grep /hello/i, #{ $hash{array} }) {
print "\#array contains 'hello'\n";
}

The usage is indeed
if (grep(/hello/i, #array)) { ... }
But according to the comments, you don't have a named array. You have a reference to an array. As such, you replace #array with an array dereference.
if (grep(/hello/i, #$array_ref)) { ... }
That's short for
if (grep(/hello/i, #{ $array_ref })) { ... }
Since your reference comes from a hash, you could also do
if (grep(/hello/i, #{ $hash{$key} })) { ... }

The Array itself is inside a hash:
hash => {
array => ['hello', 'world'],
value => 'feature',
}
Use Data::Dumper to see how exactly you've defined your structure:
use Data::Dumper;
use feature qw(say); # Highly recommend "say" when using Data::Dumper!
my %hash = (
array => ['hello', 'world'],
value => 'feature',
);
...
say Dumper \%hash;
And see what prints out. (Note the backslash in front, so you're passing in a single reference_ and not a list of values).
What you'll see is something like this:
$var = {
'array' => [
'hello',
'world',
]
}
That array isn't just an array, it's a reference to an array. You need to dereference it to get it to work:
if ( grep {/hello/i } #{ $hash->{array} } )
Or...
my #array = #{ $hash->{array} );
if ( grep { /hello/i } #array;

Related

PERL - Remove certain data form array after a symbol

my code looks like this
foreach ($id->{deleteids}) {
my #separated = split('_', $_);
push #rids, $separated[0];
}
on using Data::Dumper on $id->{deleteids} i get this
$VAR1 = [
'43-173739_cdfvgbvvd',
'43-173738_sddsvfdvfd',
'43-173737_sfvdfvdfvdf',
'43-173736_svdvdfvdfvdfvfdvfd'
];
My expected output of #rids that i want
$VAR1 = [
'43-173739',
'43-173738',
'43-173737',
'43-173736'
];
but on using Data::Dumper on #rids i always get
$VAR1 = 'ARRAY(0x3210010)';
Array references have different way to be called with, And as Zdim pointed you need to use a reference \#rids to dump array using Dumper
use strict;
use warnings;
use Data::Dumper;
my $id = { # creating a similar array like yours in a hash ref
deleteids => [
'43-173739_cdfvgbvvd',
'43-173738_sddsvfdvfd',
'43-173737_sfvdfvdfvdf',
'43-173736_svdvdfvdfvdfvfdvfd'
]
};
print Dumper($id->{deleteids});
my #rids;
foreach (#{$id->{deleteids}}) { # correct way to use array ref
my #separated = split('_', $_);
push #rids, $separated[0];
}
print Dumper(\#rids); # how to dump array using Dumper
Output:
# $id hash ref
$VAR1 = [
'43-173739_cdfvgbvvd',
'43-173738_sddsvfdvfd',
'43-173737_sfvdfvdfvdf',
'43-173736_svdvdfvdfvdfvfdvfd'
];
# #rids
$VAR1 = [
'43-173739',
'43-173738',
'43-173737',
'43-173736'
];
Following piece of code should do what you expect (remove undesired part)
foreach ($id->{deleteids}) {
s/_.*//;
push #rids, $_;
}

Passing hash slice to subroutine expecting array reference

Consider the following perl script:
use strict;
use warnings;
use Data::Dumper;
# Expects an array reference and an undef replacement value
sub undef_to_value(\#$) {
my $array_ref = shift(#_);
my $default = shift(#_);
# Map each element in the array referenced by $array_ref to...
# If the element is defined, itself
# Otherwise, the $default value
return map {
defined($_) ? $_ : $default
} #{ $array_ref };
}
my %grades = ("Alice" => 100, "Bob" => 85, "Carol" => 92);
my #students = ("Alice", "Bob", "Eve");
The following code works as expected:
my #student_grades = #grades{ #students };
#student_grades = undef_to_value(#student_grades, 0);
print Dumper(\#student_grades);
# $VAR1 = [
# 100,
# 85,
# 0
# ];
However, trying to pass a hash slice results in Type of arg 1 to main::undef_to_value must be array (not hash slice):
my #student_grades = undef_to_value( #grades{ #students }, 0 );
How is it that a hash slice can be coaxed into an array with assignment, but not during a subroutine call?
Is there any way to get the failing example to work as a single assignment?
How is it that a hash slice can be coaxed into an array with assignment
It's not. A hash slice in list context evaluates to a number of scalars, and the list assignment operator is perfectly happy with that.
In other words,
#L{qw( a b )} = #R{qw( a b )};
is equivalent to
($L{a}, $L{b}) = ($R{a}, $R{b});
As your comment says, undef_to_value expects an array reference. The prototype provides this if you provide an array, but you are providing" an hash slice instead. That's not a type a variable, so you can't take a reference to it[1].
Just accept scalars instead:
sub undef_to_value {
my $default = shift(#_);
return map { $_ // $default } #_;
}
my #student_grades = undef_to_value(0, #grades{ #students });
Of course, you could simply use
my #student_grades = map { $_ // 0 } #grades{ #students };
\#h{LIST} is equivalent to map { \$_ } #h{LIST}.
You can fool the prototype by referencing and then dereferencing an arbitrary list
undef_to_value( #{[#grades{ #students }]}, 0 );
but this will only modify a copy of your input, so it's not that helpful.
undef_to_value( #{[#grades{ #students }]}, 0 );
print Dumper([#grades{#students}]);
---
$VAR1 = [
100,
85,
undef
];
Fortunately, your undef_to_value function also returns the set of updated values, so you can say
#grades{#students} = undef_to_value( #{[#grades{ #students }]}, 0 );
print Dumper(\%grades);
---
$VAR1 = {
'Bob' => 85,
'Carol' => 92,
'Eve' => 0,
'Alice' => 100
};

access hash member of type array within array of hashes

I have an array of anonymous hashes like this:
my #arrayOfHashes=(
{
name => 'foo',
value => ['one', 'two']
},
{
name => 'bar',
value => ['two', 'three']
}
);
I'm trying to iterate over the array and access the array within each hash:
foreach (#arrayOfHashes) {
print $_->{'value'} # ARRAY(0x88489f4)
}
The value that is printed above is not what I want... I want to use that array so it works like this:
print qw(one two) # onetwo
But, when I use qw like this:
my #arrayOfHashes=(
{
name => 'foo',
qw(one two)
},
{
name => 'bar',
qw(three four)
}
);
I get this error message at runtime (I am using strict mode):
Odd number of elements in anonymous hash at ...
How do I reference the "value" array within the foreach block?
So you have a reference to an array you want to dereference. The equivalent of #array for when you have a reference is #{ $ref }, so
print("#array\n");
print(join(', ', #array), "\n");
would be
print("#{ $_->{value} }\n");
print(join(', ', #{ $_->{value} }), "\n");
References:
Mini-Tutorial: Dereferencing Syntax
References quick reference
perlref
perlreftut
perldsc
perllol

Perl multi hash from list

I have a list with some values which are connected. I need to create a hashmap with keys and values from the list and merge together. But i don't really know how to do it.
Input:
my #in =(
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat');
Expected output:
"{ $env => { $ver => [ $file1, $file2, ... ] } }"
I've tried these:
(1)
my #sack_files = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat');
my $sack_tree = {};
my %hash=();
for( my $i=0; $i<scalar #sack_files; $i++){
my #array = split(/[\/]+/,$sack_files[$i]);
for(my $i=0;$i<(scalar #array)-1;$i++){
my $first = $array[$i];
my $second = $array[$i+1];
$hash{$first}=$second;
}
# merge
}
(2)
use Data::Dumper;
my #sack_files = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat',
);
my $sack_tree = {};
my %hash=();
for( my $i=0; $i<scalar #sack_files; $i++){
my #array = split(/[\/]+/,$sack_files[$i]);
nest(\%hash,#array);
}
In the second case I get an error because when the loop variable i=1 ,the key/values already exists so maybe i have to check the previously added key/values. But I don't really know how.
I would really appreciate any ideas.
Just use push to add new members to an existing array in a hash of hashes. You have to dereference the array reference with #{ ... }.
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my #sack_files = qw( mgenv/1_2_3/parent.dx_environment
mgenv/1_2_3/doc/types.dat
mgenv/1_2_3/doc/etc.dat
mgenv/4_5_6/parent.dx_environment
mgenv/4_5_6/doc/types.dat
u5env/1_2_3/parent.dx_environment
u5env/1_2_3/doc/types.dat
u5env/4_5_6/parent.dx_environment
u5env/4_5_6/doc/types.dat
);
my %hash;
for my $sack_file (#sack_files) {
my ($env, $ver, $file) = split m{/}, $sack_file, 3;
push #{ $hash{$env}{$ver} }, $file;
}
print Dumper \%hash;
output
$VAR1 = {
'mgenv' => {
'1_2_3' => [
'parent.dx_environment',
'doc/types.dat',
'doc/etc.dat'
],
'4_5_6' => [
'parent.dx_environment',
'doc/types.dat'
]
},
'u5env' => {
'4_5_6' => [
'parent.dx_environment',
'doc/types.dat'
],
'1_2_3' => [
'parent.dx_environment',
'doc/types.dat'
]
}
};

Hashes array elements copy

My array of hashes:
#cur = [
{
'A' => '9872',
'B' => '1111'
},
{
'A' => '9871',
'B' => '1111'
}
];
Expected result:
#curnew = ('9872', '9871');
Any simple way to get only the values of the first hash element from
this and assign it to an array?
Mind that hashes are unordered, so I take the word first to mean lexicographically first.
map { # iterate over the list of hashrefs
$_->{ # access the value of the hashref
(sort keys $_)[0] # … whose key is the first one when sorted
}
}
#{ # deref the arrayref into a list of hashrefs
$cur[0] # first/only arrayref (???)
}
The expression returns qw(9872 9871).
Assigning an arrayref to an array as in #cur = […] is probably a mistake, but I took it at face value.
Bonus perl5i solution:
use perl5i::2;
$cur[0]->map(sub {
$_->{ $_->keys->sort->at(0) }
})->flatten;
The expression returns the same values as above. This code is a bit longer, but IMO more readable because the flow of execution goes strictly from top to bottom, from left to right.
First your array have to be defined as
my #cur = (
{
'A' => '9872',
'B' => '1111'
},
{
'A' => '9871',
'B' => '1111'
}
);
Note the parenthesis
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dump qw(dump);
my #cur = (
{
'A' => '9872',
'B' => '1111'
},
{
'A' => '9871',
'B' => '1111'
}
);
my #new;
foreach(#cur){
push #new, $_->{A};
}
dump #new;
use Data::Dumper;
my #hashes = map (#{$_}, map ($_, $cur[0]));
my #result = map ($_->{'A'} , #hashes);
print Dumper \#result;

Resources