Push array to a certain hash within an array in Perl - arrays

I want to dynamically push values of hashes into an array of hashes in Perl.
I have this code block to create and push classHash to an array classList.
$courseName = <STDIN>;
$section = <STDIN>;
my $classHash = {};
$classHash->{courseName} = $courseName;
$classHash->{section} = $section;
push #classList, $classHash;
Now, I want to add a studentHash to the classHash.
for my $i ( 0 .. $#classList ) {
#I want to add the studentHash to a specific classHash in the classList
if($courseName1 eq $classList[$i]{courseName} && $section1 eq $classList[$i]{section}){
$studName = <STDIN>;
$studNum = <STDIN>;
my $studHash = {};
$studHash->{studName} = $studName;
$studHash->{studNum} = $studNum;
push #studList, $studHash;
push #{$classList[$i]}, \#studList; #but this creates an array reference error
}
}

Ignoring the interactive bits... here is how you can add the student to the class:
#!/usr/bin/env perl
use warnings;
use strict;
use Data::Dumper;
my #classList = (
{
courseName => 'Algebra',
section => 101,
students => [],
},
{
courseName => 'Geometry',
section => 102,
students => [],
},
);
my $studName = 'Alice';
my $studNum = 13579;
my $desiredClass = 'Geometry';
my $desiredSection = 102;
for my $class (#classList) {
if ($class->{courseName} eq $desiredClass and
$class->{section} eq $desiredSection) {
# Add student to the class
my $student = {
studName => $studName,
studNum => $studNum,
};
push #{ $class->{students} }, $student;
}
}
print Dumper \#classList;
# Printing out the students for each class
for my $class (#classList) {
my $course = $class->{courseName};
my $section = $class->{courseSection};
my $students = $class->{students};
my $total_students = scalar #$students;
my $names = join ', ', map { $_->{studName} } #$students;
print "There are $total_students taking $course section $section.\n";
print "There names are [ $names ]\n";
}
Output
VAR1 = [
{
'students' => [],
'section' => 101,
'courseName' => 'Algebra'
},
{
'students' => [
{
'studNum' => 13579,
'studName' => 'Alice'
}
],
'section' => 102,
'courseName' => 'Geometry'
}
];
There are 0 students taking Algebra section 101.
There names are [ ]
There are 1 students taking Geometry section 102.
There names are [ Alice ]

Related

How to iterate through an Array of hashes in Perl?

I have the following array:
ifNameList -> $VAR1 = [
{
'VALUE' => ' gpon_olt-1/1/1',
'ASN1' => '285278465'
},
{
'VALUE' => ' gpon_olt-1/1/2',
'ASN1' => '285278466'
},
{
'VALUE' => ' gpon_olt-1/1/3',
'ASN1' => '285278467'
},
{
'VALUE' => ' gpon_olt-1/1/4',
'ASN1' => '285278468'
},
{
'VALUE' => ' gpon_olt-1/1/5',
'ASN1' => '285278469'
},
]
I need to iterate through this array of hashes comparing the "VALUE" field of each hash, until it matches and do some action.
I've already made the following code, but its not working. What I'm doing wrong?
sub GetIfIndexFromName{
my $ifName = shift;
my #ifList = shift;
my $index;
for (#ifList){
my %interfaceHash = %$_;
# Just trims any blank space on the string:
$interfaceHash->{"VALUE"} =~ s/^\s+|\s+$//g;
if($interfaceHash->{"VALUE"} eq $ifName){
print "trimmed interface name-> ".$interfaceHash->{"VALUE"}."\n\n";
$index = $interfaceHash->{"ASN1"};
}
}
print "Returning index value: ".$index;
return $index;
}
Two errors.
Problem 1: Wrong variable
ALWAYS use use strict; use warnings;. It would have found this error:
# Access the `VALUE` element of the hash referenced by `$interfaceHash`.
$interfaceHash->{"VALUE"}
You have no variable named $interfaceHash.
There are three ways to fix this:
for ( #ifList ) {
my %interfaceHash = %$_;
... $interfaceHash{ VALUE } ...
}
for my $interfaceHash ( #ifList ) {
... $interfaceHash->{ VALUE } ...
}
The latter is recommended. It avoids creating a copy of the hash, which involves create a number of temporary scalars. This is all useless work.
Problem 2: Incorrect parameter retrieval
The following is wrong:
my #ifList = shift;
shift returns a scalar. There's absolutely no point in using an array to hold exactly one scalar at all times.
sub GetIfIndexFromName {
my $ifName = shift;
my $ifList = shift;
for ( #$ifList ) {
...
}
}
# Pass a reference to the array.
GetIfIndexFromName( $ifName, $VAR1 )
sub GetIfIndexFromName {
my $ifName = shift;
my #ifList = #_;
for ( #ifList ) {
...
}
}
# Pass each element of the array.
GetIfIndexFromName( $ifName, #$VAR1 )
The former convention is more efficient, but the latter can create cleaner code in the caller. Probably not in your program, though.
How I'd write this:
use strict;
use warnings;
use feature qw( say );
use List::Util qw( first );
sub trim_inplace { $_[0] =~ s/^\s+|\s+\z//g; }
my #ifList = ...;
my $ifName = ...;
trim_inplace( $_->{ VALUE } ) for #ifList;
my $match = first { $_->{ VALUE } eq $ifName } #ifList
or die( "Interface not found.\n" );
my $asn1 = $match->{ ASN1 };
say $asn1;

perl call variable name using another variable(array)

my #CLASS_TYPES = ("INTRA", "BB", "CAT");
my #INTRA_NEIGH = ("1.1.1.1/32","2.2.2.2/32");
my #BB_NEIGH = ("3.3.3.3/32","4.4.4.4/32" );
foreach my $class (#CLASS_TYPES) {
my $csv = #.$class._NEIGH;
print($csv);
when I print $csv I am expecting to print the array values how do I achieve that
That's not the right approach. See Why it's stupid to `use a variable as a variable name' and A More Direct Explanation of the Problem.
The very fact that #CAT_NEIGH doesn't exist illustrates part of the problem.
Solution:
my #CLASS_TYPES = ("INTRA", "BB", "CAT");
my %NEIGH = (
INTRA => [ "1.1.1.1/32", "2.2.2.2/32" ],
BB => [ "3.3.3.3/32", "4.4.4.4/32" ],
);
for my $class (#CLASS_TYPES) {
next if !$NEIGH{$class};
print "$_\n" for #{ $NEIGH{$class} };
}
or just
my %NEIGH = (
INTRA => [ "1.1.1.1/32", "2.2.2.2/32" ],
BB => [ "3.3.3.3/32", "4.4.4.4/32" ],
);
for my $class (keys(%NEIGH)) {
print "$_\n" for #{ $NEIGH{$class} };
}

Iterating over an array of hashes inside of a hash

I am new to Perl and I am having a hard time with iterating over an array of hashes inside of a hash. I tried this solutionHere is the data structure from Dumper:
1234 => {
'items' => [
#0 {
'k1' => { 'x1' => '123', 'x2' => '321' },
'k2' => 'v2',
'k3' => 'v3',
'k4' => 'v4'
},
#1 {
'k1' => { 'x1' => '123', 'x2' => '321' },
'k2' => 'v2',
'k3' => 'v3',
'k4' => 'v4'
},
#2 {
'k1' => { 'x1' => '123', 'x2' => '321' },
'k2' => 'v2',
'k3' => 'v3',
'k4' => 'v4'
},
],
}
Here is the code I have. I want to iterate over the hash '%myHash' while having access to both the keys and values. Then for each hash inside the "items" array I want to perform some operation.
In this case, my key is '1234' and its value is a hash that contains the key 'items'. This is the code I have (a ref to the hash is being passed to my sub):
sub iterateHash {
my %myHash = #_;
while (my ($key, $value) = each %myHash) {
my %newHash;
foreach my $item (#{ $value->{'items'} }) {
if( !(defined $newHash{'nk1'}) ) {
$newHash{'nk1'} = $item{'k1'}{'x2'};
}
}
}
}
and how it is called:
my $results = iterateHash(\%myHash);
I have uses for the key later on which is why I want access to both the key and value in a pair in the outer while loop, but for now I want to get this part working.
This is the error I get:
Global symbol "%item" requires explicit package name
You passed a reference but didn't treat it like a reference inside iterateHash. Also, once you get into nested structures, everything is a reference, so $item is a hash reference not a plain hash.
sub iterateHash {
my ($myHash) = #_;
while (my ($key, $value) = each %$myHash) {
my %newHash;
foreach my $item (#{ $value->{'items'} }) {
if( !(defined $newHash{'nk1'}) ) {
$newHash{'nk1'} = $item->{'k1'}->{'x2'};
}
}
}
}

Convert an array of strings into a array of arrays of strings

My goal is to convert this
my #array=("red", "blue", "green", ["purple", "orange"]);
into this
my #array = ( ["red"], ["blue"], ["green"], ["purple", "orange"]);
Current test code:
my #array = ("red", "blue", "green", ["purple", "orange"] );
foreach $item ( #array ) {
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
if(ref($item) ne 'ARRAY'){
#It's an array reference...
#you can read it with $item->[1]
#or dereference it uisng #newarray = #{$item}
#print "We've got an array!!\n";
print $item, "\n";
# keep a copy of our string
$temp = $item;
# re-use the variable but convert to an empty list
#item = ();
# add the temp-copy as first list item
#item[0] = $temp;
# print each list item (should be just one item)
print "$_\n" for $item;
}else{
#not an array in any way...
print "ALREADY an array!!\n";
}
}
# EXPECTED my #array=(["red"], ["blue"], ["green"], ["purple", "orange"]);
print #array , "\n";
foreach $item (#array){
if(ref($item) ne 'ARRAY'){
#
#say for $item;
print "didn't convert properly to array\n";
}
}
The comment about python maps pretty directly to perl.
my #array = ("red", "blue", "green", ["purple", "orange"] );
foreach $item ( #array ) {
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
if (ref $item ne 'ARRAY') {
$item = [ $item ];
}
}
though using map as in Borodin's answer would be more natural.
I'm wondering why you want to do this, but it's
#array = map { ref ? $_ : [ $_ ] } #array
And please don't call arrays #array; that's what the # is for.
Your comment is ridiculous
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
If you were familiar with Perl then you wouldn't need to ask the question. You must be aware that there is no one-to-one translation from Python to Perl. Python is much less expressive than either Perl or C, but I can't imagine you demanding a simple conversion to C.
Please get over your bigotry.
If you push the values to a new array, you don't need to do more than evaluate whether or not $item is an arrayref:
#! perl
use strict;
use warnings;
use Data::Dumper;
my #array=("red", "blue", "green", ["purple", "orange"]);
my #new_array;
foreach my $item (#array) {
if ( ref($item) eq 'ARRAY' ) {
push #new_array, $item;
}
else {
push #new_array, [$item];
}
}
print Dumper \#new_array;
Output from Dumper:
$VAR1 = [
[
'red'
],
[
'blue'
],
[
'green'
],
[
'purple',
'orange'
]
];
After a long day of learning more Perl than I ever thought/wanted to learn... here's what I think is a workable solution:
#! perl
use strict;
use warnings;
use Data::Dumper;
my %the_dict = (duts =>
{dut_a => {UDF => 'hamnet'},
dut_b => {UDF => [ '1', '2', '3', ]},
dut_c => {UDF => [ 'Z' ], }});
print Dumper \%the_dict;
foreach my $dut (keys %{$the_dict{duts}}) {
# convert the dut's UDF value to an array if it wasn't already
if ( 'ARRAY' ne ref $the_dict{duts}->{$dut}{UDF} ) {
$the_dict{duts}->{$dut}{UDF} = [ $the_dict{duts}->{$dut}{UDF} ];
}
# now append a new value to the array
push(#{$the_dict{duts}{$dut}{UDF}}, 'works');
}
print Dumper \%the_dict;
when run we see these print-outs:
$VAR1 = {
'duts' => {
'dut_a' => {
'UDF' => 'hamnet'
},
'dut_c' => {
'UDF' => [
'Z'
]
},
'dut_b' => {
'UDF' => [
'1',
'2',
'3'
]
}
}
};
$VAR1 = {
'duts' => {
'dut_a' => {
'UDF' => [
'hamnet',
'works'
]
},
'dut_c' => {
'UDF' => [
'Z',
'works'
]
},
'dut_b' => {
'UDF' => [
'1',
'2',
'3',
'works'
]
}
}
};

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

Resources