PERL - Remove certain data form array after a symbol - arrays

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, $_;
}

Related

Clean way to access a nested data structure

I have a segment of code that, although it works, does not look like a clean way to do things.
I build the structure using:
foreach my $n (#node_list)
{
chomp ($n);
foreach my $c (#cpes)
{
my #returned; #Interfaces to CPEs with MED settings
my #creturned; #General Customer Interfaces
my ($cust) = $c =~ /([a-zA-Z]+)[_-][a-zA-Z0-9]+/s;
print "\n\t\tCustomer is $cust\n";
chomp($c);
$c = uc $c;
my ($search) = $c;
(#returned) = `cat /curr/$n | grep "$search"`;
if (#returned)
{
my $cust_match = 'interface \"' . $cust;
(#creturned) = `cat /curr/$n | egrep -i "$cust_match" | grep -v "$search"`;
}
if (#creturned) #Have we found other CPEs on the same router
{
my ($nf) = $n =~ /([a-zA-Z0-9-]+).cfg/s;
my (#interfaces) = map { /([A-Z0-9_]+)/s } #creturned;
#interfaces = uniq(#interfaces);
unshift (#interfaces, $c);
push (#new_out, {$nf => {$cust => [#interfaces]}});
}
}
This will return:
$VAR1 = [
{
'router-xx-xx' => {
'50000' => [
[
'THXXXXVF_NLXXXX40_1121_2',
'10x.xx.x.50'
],
[
'THXXXPVF_NLXXXX66_1121_1',
'10x.xx.x.70'
],
[
'THXXXXVF_NLXXXX67_1121_2',
'10x.xx.x.78'
],
}
},
Each router can have a number of VPRNs and each VPRN can contain multiple interfaces. In the example above I've shown one router with one VPRN.
However, when it comes to accessing elements in the above, I've written the following convoluted (but working) code:
foreach my $candidate (#nodes)
{
my %node = %{ $candidate };
foreach my $n (keys %node)
{
print "\nRouter is $n\n";
foreach my $cust (keys %{ $node{$n} })
{
print "Customer on $n is \n" . Dumper $cust;
my #intlist = #{$node{$n}{$cust}};
my $med_cpe = $intlist[0]; #the CPE that was used to find node
{truncated}
}
}
}
}
You don't explain exactly what you find "convoluted" about the traversal code, but you have made it unnecessarily complex by duplicating data into #intlist and %node. The excessive and inconsistent indentation also makes it ungainly
I would write something closer to this
for my $node ( #nodes ) {
for my $n ( keys %$node ) {
print "\nRouter is $n\n";
for my $cust ( keys %{ $node->{$n} } ) {
print "Customer on $n is \n" . Dumper \$cust;
my $med_cpe = $node->{$n}{$cust}[0];
}
}
}
If you don't need the values of $node and $n except to access $med_cpe then you don't need a nested data structure at all: a simple array is fine. On the face of it, an array like this will do what you need
[
[
'router-xx-xx',
'50000',
'THXXXXVF_NLXXXX40_1121_2',
'10x.xx.x.50',
],
[
'router-xx-xx',
'50000',
'THXXXPVF_NLXXXX66_1121_1',
'10x.xx.x.70',
],
...
]

Passing an array to a subroutine encloses it in another array?

I noticed that when I pass an array to my subroutine it seems like it gets encapsulated by another array (so two levels, while the initial is only one).
I know that using references to arrays is better, but I'm wondering in this specific case why it is not working as expected.
Code example:
#!/usr/local/bin/perl
use Data::Dumper;
sub testSub {
my (#arr) = (#_);
print Dumper \#arr;
}
my #testArray = ();
push #testArray, {
'key1' => 'value1',
'key2' => 'value2',
'urls' => [ 'www.example.com' ]
};
print Dumper #testArray;
foreach my $item ( #testArray ) {
my #urls = testSub( $item->{'urls'} );
}
output
$VAR1 = {
'urls' => [
'www.example.com'
],
'key1' => 'value1',
'key2' => 'value2'
};
$VAR1 = [
[
'www.example.com'
]
];
my #urls = testSub( $item->{'urls'}, 'abc' );
Result of Dumper in subrotine:
$VAR1 = [
[
'www.example.com'
],
'abc'
];
Array passed by reference. Since at the time of compilation perl did not know what will be in the scalar $item->{'urls'}.
my #urls = testSub( #{ $item->{'urls'} }, 'abc' );
Result of Dumper in subrotine:
$VAR1 = [
'www.example.com',
'abc'
];
Now the compiler expects an array and turns it into a list.
You are passing $item->{'urls'} to your subroutine
Your Data::Dumper output clearly shows that the hash element looks like this
'urls' => [ 'www.example.com' ]
When you call testSub, you are making an assignment that is equivalent to
my #arr = ( [ 'www.example.com' ] );
Your statement print Dumper \#arr passes an array reference to Dumper, so it displays
[ [ 'www.example.com' ] ]
It would help your confusion if you were consistent in calling Dumper. print Dumper #testArray passes the contents of #testArray as individual parameters (although in this case the array has only a single element) while print Dumper \#arr passes an array reference as a single parameter, and is the better choice
If you want testSub to receive a list of URLs, you must expand the array $item->{urls} into a list with #{ ... }:
my #urls = testSub( #{ $item->{'urls'} } );

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'
]
}
};

Grep not returning expected value Perl

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;

perl hash with array

I did same hash like this:
my %tags_hash;
Then I iterate some map and add value into #tags_hash:
if (#tagslist) {
for (my $i = 0; $i <= $#tagslist; $i++) {
my %tag = %{$tagslist[$i]};
$tags_hash{$tag{'refid'}} = $tag{'name'};
}}
But I would like to have has with array, so when key exists then add value to array.
Something like this:
e.g. of iterations
1,
key = 1
value = "good"
{1:['good']}
2,
key = 1
value = "bad"
{1:['good', 'bad']}
3,
key = 2
value = "bad"
{1:['good', 'bad'], 2:['bad']}
And then I want to get array from the key:
print $tags_hash{'1'};
Returns: ['good', 'bad']
An extended example:
#!/usr/bin/perl
use strict;
use warnings;
my $hash = {}; # hash ref
#populate hash
push #{ $hash->{1} }, 'good';
push #{ $hash->{1} }, 'bad';
push #{ $hash->{2} }, 'bad';
my #keys = keys %{ $hash }; # get hash keys
foreach my $key (#keys) { # crawl through hash
print "$key: ";
my #list = #{$hash->{$key}}; # get list associate within this key
foreach my $item (#list) { # iterate through items
print "$item ";
}
print "\n";
}
output:
1: good bad
2: bad
So the value of the hash element to be an array ref. Once you have that, all you need to do is push the value onto the array.
$hash{$key} //= [];
push #{ $hash{$key} }, $val;
Or the following:
push #{ $hash{$key} //= [] }, $val;
Or, thanks to autovivification, just the following:
push #{ $hash{$key} }, $val;
For example,
for (
[ 1, 'good' ],
[ 1, 'bad' ],
[ 2, 'bad' ],
) {
my ($key, $val) = #$_;
push #{ $hash{$key} }, $val;
}

Resources