Dynamical array as a class member - arrays

There is a following class:
package MyClass;
use strict;
use warnings;
sub new
{
my $class = shift();
my $self = {
_class_array => []
};
bless ($self, $class);
return $self;
}
How can I set/get add values to this array?
I tried the following code:
sub AddDataType
{
my $self = shift();
my $new_element = shift();
my #array = $self->{_class_array};
print("Number of elements ".($self->{_class_array})."\n");
push(#array, $new_element);
$self->{_class_array} = #array;
print("Element added. Number of elements ".($self->{_class_array})."\n");
}
The output is the following:
Number of elements ARRAY(0x21bb4c)
Element added. Number of types 2
Number of elements 2
Element added. Number of types 2
Number of elements 2
Element added. Number of types 2
Questions are:
What does that mean: Number of elements ARRAY(0x21bb4c)?
Why array length always stays 2?

You are using an arrayref as an array. Try:
sub AddDataType {
my ( $self, $new_element ) = #_;
print "Number of elements " . scalar #{ $self->{_class_array} } . "\n";
push #{ $self->{_class_array} }, $new_element;
print "Element added. Number of elements " . scalar #{ $self->{_class_array} } . "\n";
return;
}

As others have noted, your array class element is an array reference. Your method should look like this
sub AddDataType {
my ($self, $new_element) = #_;
my $array = $self->{_class_array};
print "Number of elements " . scalar #$array . "\n";
push #$array, $new_element;
print "Element added. Number of elements " . scalar #$array . "\n";
}

You got good answers already. I just want to mention that Moose traits can really make this kind of attribute stuff simple/fun.
BEGIN {
package MyClass;
use Moose;
has "data" =>
traits => ["Array"],
is => "ro",
isa => "ArrayRef[Str]",
default => sub { [] },
handles => {
AddDataType => "push",
DataCount => "count",
NoData => "is_empty",
AllData => "elements",
};
}
my $thingy = MyClass->new();
print "DOES HAS DATAS? ", $thingy->NoData ? "NOE" : "YES", $/;
$thingy->AddDataType("OHAI");
print "CAN HAS DATA? ", $thingy->NoData ? "NOE" : "YES", $/;
$thingy->AddDataType(qw/ ANUDDER CUPLA HERE / );
print "I HAZ DATAS: ", $thingy->DataCount, $/;
print "HERE DEY IS: ", join(", ", $thingy->AllData), $/;
__DATA__
DOES HAS DATAS? NOE
CAN HAS DATA? YES
I HAZ DATAS: 4
HERE DEY IS: OHAI, ANUDDER, CUPLA, HERE

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;

Generating a unordered list hash from a array

I am trying to use hashes to generate an unordered list that i can further use in a jstree. But this array has to be generated only from an array that has been passed thru .
my #array = ( "New Order","Recurring Order","Previously Cancelled Order");
I want the output to look something like
$data = {
"New Order" => {
"Recurring Order" =>{
Previously cancelled Order = 1
}
}
};
I can simply do
my $data{$array[0]}{$array[1]}{$array[2]} = 1
but the array can be of n variables, so it becomes a bit more complicated than that. I am thinking of recursion, but i have been sitting here for the last hour trying to figure that out
This will generate the data structure as you have defined it. Not sure why you'd want it though.
my #input = ( "New Order","Recurring Order","Previously Cancelled Order");
my $data = 1;
$data = {$_ => $data} for reverse #input;
use Data::Dump;
dd $data;
If you're just wanting to randomize your array, then use List::Util;
use List::Util qw(shuffle);
my #newOrder = shuffle #input;
sub recursive {
my $v = shift #_;
return #_>1 ? { $v => recursive(#_) } : { $v => #_ };
}
my #array = ( "New Order","Recurring Order","Previously Cancelled Order");
use Data::Dumper; print Dumper recursive(#array, 1);
output
$VAR1 = {
'New Order' => {
'Recurring Order' => {
'Previously Cancelled Order' => 1
}
}
};

Iterate through a hash and an array in Perl

I have an array and a hash:
#arraycodons = "AATG", "AAAA", "TTGC"... etc.
%hashdictionary = ("AATG" => "A", "AAAA" => "B"... etc.)
I need to translate each element of the array for the corresponding value in hashdictionary. However, I obtain a wrong translation.....
To see the problem, I have printed $codon (each element of the array), but each codon is repeated several times..... and It shouldn't.
sub translation() {
foreach $codon (#arraycodons) {
foreach $k (keys %hashdictionary) {
if ($codon == $k) {
$v = $hashdictionary{$k};
print $codon;
}
}
}
}
I don't know if I've explained my problem well enough, but I can't go on with my code if this doesn't work...
Many thanks in advance.
You appear to be looping through the keys of your hash (also known as a "dictionary") to find your desired key. This defeats the purpose of a hash (also known as a "dictionary") - the primary advantage of which is ultra fast lookups of a key.
Try, instead of
foreach $codon (#arraycodons) {
foreach $k (keys %hashdictionary) {
if ($codon == $k) {
$v = $hashdictionary{$k};
print $codon;
}
}
}
this:
foreach $codon (#arraycodons) {
my $value = $hashdictionary{$codon};
print( "$codon => $value\n" );
}
or:
foreach my $key ( keys %hashdictionary ) {
my $value = $hashdictionary{$key};
print( "$key => $value\n" );
}
my #mappedcodons = map {$hashdictionary{$_}}
grep (defined $hashdictionary{$_},#arraycodons);
or
my #mappedcodons = grep ($_ ne "", map{$hashdictionary{$_} || ""} #arraycodons);
my #words = ("car", "house", "world");
my %dictionary = ("car" => "el coche", "house" => "la casa", "world" => "el mundo");
my #keys = keys %dictionary;
foreach(#words) {
my $word = $_;
foreach(#keys) {
if($_ eq $word) { # eq, not ==
my $translation = $dictionary{$_};
print "The Spanish translation of $word is $translation\n";
}
}
}

perl hash of arrays

I am trying to access elements of an array which is part of a hash.
for my $idx ( 0 .. $#vss ) {
push (#{$vsnhash->{$vss[$idx]}}, $vsports[$idx]);
}
print Dumper(\%$vsnhash);
($VAR1 = {
'name2' => [
'8001',
'8002'
],
'name1' => [
'8000'
]
};
I an able to access the keys with a foreach loop:
foreach my $key ( keys %$vsnhash ) {
print "$key\n";
}
How do I access the array of port numbers ('8001' , '8002') within the hash?
Thank you for the help!
while (my ($k, $v) = each %$vsnhash) {
print "$k: #$v\n";
}
foreach my $key ( keys %$vsnhash ) {
print "$key\n";
foreach my $port (#{$vsnhash->{key}}){
print "Port $port\n";
}
}
$vsnhash{name2}->[0]; #8001
$vsnhash{name2}->[1]; #8002
$vsnhash{name1}->[0]; #8000
Code wise:
foreach my $key (sort keys %vsnhash) {
foreach my $index (0..$#{$key}) {
print "\$vsnhash{$key}->[$index] = " . $vsnhash{$key}->[$index] . "\n";
}
}
The $#{$key} means the last entry in the array #{$key}. Remember that $key is a reference to an array while #{$key} is the array itself.

Another simple Perl question...(hash to an array of objects)

I'm working on creating a Graph class in Perl and I'm hoping to have each Graph contain Nodes. Each Node has some properties and also (for now) an array which contains references to each other node it is connected to. So far I have this definition of a node:
use strict;
package Node;
sub new{
my $class = shift;
my #array = ();
my $array_r = \#array;
my $self = {
code => undef,
name => undef,
country => undef,
continent => undef,
timezone => undef,
coordinates => undef
population => undef,
region => undef,
arrayRef => $array_r,
#_,
};
bless $self, $class;
return $self;
}
Yet upon calling the following function from my main script:
sub getSetArray{
my $self = shift;
my $param = shift;
my $temp = $self->{arrayRef};
push(#{$temp}, $param) if defined $param;
return $self->{arrayRef};
}
and trying to iterate over a Node's array(which will contain more Nodes that it is connected to):
my $firstnode = Node->new(); # Node constructor should have #array of
my $secondnode = Node->new();
$firstnode->getSetCode("test");
print "The current code is ", $firstnode->getSetCode(), "\n";
my $array_r = $firstnode->getSetArray($secondnode);
$array_r = $firstnode->getSetArray($firstnode);
foreach my $obj (#{$array_r}){
print $obj;
}
This prints out Node=HASH(0x10092bb00)Node=HASH(0x100907bd8). Which leads me to believe that I am dealing with an array containing 2 nodes (this is what I want). But upon attempting to call any methods of this $obj's I am told that I
Can't call method "getSetNode" without a package or object reference.
I had already previously blessed these two objects when calling new on these nodes. So I'm not sure why they aren't recognized as Nodes and I can't call their methods....
EDIT -
foreach my $obj (#{$array_r}){
print $obj->getSetCode();
}
where getSetCode() is
sub getSetCode{
my $self = shift;
my $param = shift;
$self->{code} = $param if defined $param;
return $self->{code};
}
Show how you are trying to call getSetNode on $obj, and the code of getSetNode?
By the way, $temp isn't needed; you can directly do:
push #{ $self->{arrayRef} }, $param if defined $param;
Maybe helpful to you: http://perlmonks.org/?node=References+quick+reference
Also note that you are setting up a cyclical data structure ($firstnode indirectly referencing itself) that perl won't garbage collect automatically, even when all external references go away; you can fix this (if this is even a concern) with:
if (defined $param) {
push #{ $self->{arrayRef} }, $param;
Scalar::Util::weaken( $self->{arrayRef}[-1] ) if $param == $self;
}
As far as your problem goes, putting all your code together (and adding a missing , on the coordinates line) into this:
use warnings;
use strict;
package Node;
sub new {
my $class = shift;
my #array = ();
my $array_r = \#array;
my $self = {
code => undef,
name => undef,
country => undef,
continent => undef,
timezone => undef,
coordinates => undef,
population => undef,
region => undef,
arrayRef => $array_r,
#_,
};
bless $self, $class;
return $self;
}
sub getSetArray{
my $self = shift;
my $param = shift;
my $temp = $self->{arrayRef};
push(#{$temp}, $param) if defined $param;
return $self->{arrayRef};
}
sub getSetCode{
my $self = shift;
my $param = shift;
$self->{code} = $param if defined $param;
return $self->{code};
}
my $firstnode = Node->new(); # Node constructor should have #array of
my $secondnode = Node->new();
$secondnode->getSetCode("test2");
$firstnode->getSetCode("test");
print "The current code is ", $firstnode->getSetCode(), "\n";
my $array_r = $firstnode->getSetArray($secondnode);
$array_r = $firstnode->getSetArray($firstnode);
foreach my $obj (#{$array_r}){
print $obj->getSetCode(), "\n";
}
gives this output for me:
The current code is test
test2
test

Resources