I have an array called aTestCaseList which is initialized and filled with (Moose) objects of type "Testcase". As expected I can print out the Attribute TestName of every Testcase Object in aTestCaseList. But when I try to find the first Object in the list named "Test4" using https://perldoc.perl.org/List/Util.html#first I get the following error
Can't call method "TestName" on an undefined value
Why are the objects in the array suddenly undefined?
use Testcase;
my #aTestcaseList=();
for (my $i=1; $i <= 9; $i++) {
push(#aTestcaseList,Testcase->new("Test".$i));
}
my $sTestcase="Test4";
foreach my $sTestDummy(#aTestcaseList)
{
#Works as expected and prints: Test1 Test2 Test3 ... Test9
print $sTestDummy->TestName." ";
}
# throws the error:
my $sFindTest=first {$_->TestName eq $sTestcase} #aTestcaseList;
package Testcase;
use Moose;
use namespace::autoclean;
has 'TestName' => (is =>'ro',isa=>'Str');
around BUILDARGS => sub
{
my $orig = shift;
my $class = shift;
if ( #_ == 1 && ! ref $_[0] ) {
return $class->$orig(TestName => $_[0]);
}
else {
return $class->$orig(#_);
}
};
__PACKAGE__->meta->make_immutable;
1;
You forgot to import the function first from List::Util like
use List::Util qw(first);
When I Run createQueryBuilder Query by passing Array as a parameter value showing as array undefined.
$list = array('13','197');
$queryBuilder = function($repo) use ($user) {
return $repo->createQueryBuilder('p')
->where('p.id IN (:stores)')
->setParameter('stores',$list);
->orderBy('p.name', 'ASC');
}
You need to include your $list variable in lambda-function scope. To do that you need to provide this variable in use-part of lambda-function definition:
$list = array('13','197');
$queryBuilder = function($repo) use ($user, $list) {
return $repo->createQueryBuilder('p')
->where('p.id IN (:stores)')
->setParameter('stores',$list);
->orderBy('p.name', 'ASC');
}
I am new to Perl and I am trying to define a 2D array as an attribute of my class in Perl. I define my class as follows,
sub new{
my $class = shift;
my $self = {};
my #board = [];
for (my $i = 0; $i < 8; $i++){
for(my $j = 0; $j < 8; $j++){
$board[$i][$j] = '.';
}
}
$self->{board} = #board;
bless($self, $class);
return $self;
}
But later on when I try to access the board field like this
$self->{board}[$i][$j] = ' ';
I got an error saying
Can't use string ("8") as an ARRAY ref while "strict refs" in use
Can anyone tell me what is the correct way of doing this? I do not want to just delete use strict.
I changed your code to what I'm sure was your intention,
see the lines changed and comment # not
sub new{
my $class = shift;
my $self = {};
my #board = (); # not []
for (my $i = 0; $i < 8; $i++){
for(my $j = 0; $j < 8; $j++){
$board[$i][$j] = '.';
}
}
$self->{board} = \#board; # not #board
bless($self, $class);
return $self;
}
or
sub new{
my $class = shift;
my $self = {};
my $board = []; # not #board
for (my $i = 0; $i < 8; $i++){
for(my $j = 0; $j < 8; $j++){
$board->[$i][$j] = '.';
}
}
$self->{board} = $board; # not #board
bless($self, $class);
return $self;
}
about your my #board=[]; is the same as =([],); assign a list (that perl calls ARRAY) whose first element is a reference to an ARRAY to #board, but this is neither what make your code fail because you overwrite this empty array reference allocation and assignment to position zero.
The #board is a list not a reference to it as $self->{board} expect
You need to place a reference to array inside your $self hash. Right now you're placing a value of array in scalar context - which is its length 8. Of course you can't later use this as a reference to anything.
$self->{board} = \#board;
Others have given you explanations of what the problem is with your constructor. I'll add, that you can simplify it a bit by omitting the $board variable and using C-style loops.
sub new {
my $class = shift;
my $self = {};
$self->{board} = [];
for my $i (1 .. 8) {
for my $j (1 .. 8) {
$self->{board}[$i][$j] = '.';
}
}
return bless $self, $class;
}
I'd also add the following three methods which make it easier to set and get the elements of the board:
sub board {
my $self = shift;
return $self->{board};
}
sub set_board_element {
my $self = shift;
my ($i, $j, $val) = #_;
$self->board->[$i][$j] = $val;
}
sub get_board_element {
my $self = shift;
my ($i, $j) = #_;
return $self->board->[$i][$j];
}
Have you considered using Moose to write your class? It will make your life easier. In particular, Array traits seem like a good fit for your problem.
I have a string of data which I turn into an array thus:
$cfg_data = #(
"AppName1,data1",
"AppName2,data2"
)
I then run a foreach query to work on each line at a time:
FOREACH($a in $cfg_data)
{
$dataSplit = $a -split"(,)"
$AppN = $dataSplit[0]
$AppD = $dataSplit[2]
#Do stuff here
}
But I want to convert this from a string to an Object, so I can add/remove additional items,
and then run some more foreach statements, updating the different bits as I go.
I have got as far as:
FOREACH($a in $cfg_data)
{
$dataSplit = $a -split"(,)"
$AppN = $dataSplit[0]
$AppD = $dataSplit[2]
$objHere = #(
#{
appItem = "$AppN";
appData = "$AppD";
})
}
But when I check $objHere it just has the last entry in it (AppName2, datat2)
I tried adding:
$b=0
and
$objHere[$b]
but then I get
Array assignment failed because index '1' was out of range.
What is the correct way to do this?
By declaring $objHere in the loop, you overwrite the value on each iteration. You need to initialise an empty array outside the loop and append to it from within the loop:
$objHere = #()
foreach($a in $cfg_data)
{
$dataSplit = $a -split"(,)"
$AppN = $dataSplit[0]
$AppD = $dataSplit[2]
$objHere +=
#{
appItem = "$AppN";
appData = "$AppD";
}
}
In addition, you're not actually creating an object, you're creating a hashtable. If you wanted to create an object instead, you could do this:
$objHere = #()
foreach($a in $cfg_data)
{
$dataSplit = $a -split"(,)"
$AppN = $dataSplit[0]
$AppD = $dataSplit[2]
$objHere += New-Object PSObject -property #{appItem = "$AppN";appData = "$AppD";}
}
Giving:
appItem appData
------- -------
AppName1 data1
AppName2 data2
I have a subroutine that should take an array as input, make it into a CSV, and POST it to a URL. Here's what I've got so far:
An example array:
[ 2823383062, 1411691539, 1411691541, 'outgoing',
'SIP/fr', 'user#2000', '2000', 'SIP/2000-000000a2',
undef, '6125551234', 'SIP/fr-000000a3', undef,
undef, 8, 'Answered', 2,
1, 'nada'
];
The subroutine:
sub send_http {
my #http = #_;
my $h = LWP::UserAgent->new;
$h->timeout(15);
$h->agent(undef);
my $testkey = "1234";
my $apikey = "4567";
my $posting;
foreach my $v ( \#http ) {
if ( defined $v ) {
$posting = join( ',', $posting, $v );
} else {
$posting = join( ',', $posting, "" );
}
}
my $api_response = $h->post( 'http://url.com/v1/post.cfm',
[ key => $testkey, method => 'pushCalls', rawdata => $posting ] );
}
Forgive all the horrible things I've done; this is my first time using Perl and I'm still learning all sorts of stuff. My issue is that I can't seem to get the values from the array I pass into it past the first array variable declaration (#http). I've read something about getting a reference of the array, but am not sure where/how to do so. Any help is appreciated.
Edit:
Here's the entire script. It does (or should) two things; send some string of data to a TCP socket, and some other data gets POST'ed to a URL. Thanks everyone for your help.
#!/usr/bin/perl
use EV;
use Asterisk::AMI;
use Net::Telnet;
use HTTP::Request::Common;
use LWP::UserAgent;
use strict;
use warnings;
use Data::Dumper;
my %call;
my $t = new Net::Telnet (
Timeout => 10,
Port => '1234',
Telnetmode => 1
);
my $astman = Asterisk::AMI->new(PeerAddr => '127.0.0.1',
PeerPort => '5038',
Username => 'secret',
Secret => 'user',
Events => 'on',
Handlers => {
# default => \&eventhandler,
Dial => \&dialcheck,
Bridge => \&bridgecheck,
Newchannel => \&newchannel,
Newexten => \&newexten,
Hangup => \&hangup,
Newstate => \&outring
}
);
die "Unable to connect to asterisk" unless ($astman);
sub send_pos {
my ($pos_string,$telnet) = #_;
$telnet->open('127.0.0.1');
printf $t $pos_string;
$telnet->close()
}
sub send_http {
my $http = shift; ##_;
my $h = LWP::UserAgent->new;
$h->timeout(15);
$h->agent(undef);
my $testkey = "1234";
my $apikey = "5678";
my $posting;
foreach my $v ( #http ) {
if ( defined $v ) {
$posting = join(',', $posting,$v);
} else {
$posting = join(',', $posting,"");
}
}
my $api_response = $h->post( 'http://url.com/v1/post.cfm',[key => $testkey,method => 'pushCalls',rawdata => $posting]);
}
sub eventhandler {
# Default event handler, not used
my ($ami, $event) = #_;
print 'Got Event: ',$event->{'Event'},"\r\n";
}
sub newchannel {
my ($ami, $event) = #_;
my $unique_id = $event->{'Uniqueid'};
if ( not exists $call{$unique_id} ) {
my $this_call = $call{$unique_id};
if ( (not defined $this_call->{'gravityfree'}[3]) ) {# || ($this_call->{'gravityfree'}[3] !~ /incoming|outgoing/) ) {
if ( $event->{'Context'} =~ /from-trunk/ ) {
# Call is inbound
$this_call->{'caller_name'} = $event->{'CallerIDName'};
$this_call->{'caller_number'} = substr $event->{'CallerIDNum'}, -10;
$this_call->{'dnis'} = substr $event->{'Exten'}, -10;
$this_call->{'status'} = "remote";
$this_call->{'holdstart'} = time();
# Data required for Gravity Free
$this_call->{'gravityfree'}[0] = int($event->{'Uniqueid'})+int(time());
$this_call->{'gravityfree'}[3] = "incoming";
$this_call->{'gravityfree'}[5] = $event->{'CallerIDName'};
$this_call->{'gravityfree'}[6] = substr $event->{'CallerIDNum'}, -10;
$this_call->{'gravityfree'}[7] = $event->{'Channel'};
$this_call->{'gravityfree'}[11] = substr $event->{'Exten'}, -10;
# Can't remember why this is here:
$call{$unique_id} = $this_call;
} elsif ( $event->{'Context'} =~ /from-internal/ ) {
# Call is outbound
# Separate from calls to stations
if( length($event->{'CallerIDNum'}) < length($event->{'Exten'}) ) {
$this_call->{'status'} = "remote";
# Data required for Gravity Free
$this_call->{'gravityfree'}[0] = int($event->{'Uniqueid'})+int(time());
$this_call->{'gravityfree'}[9] = substr $event->{'Exten'}, -10;
$this_call->{'gravityfree'}[3] = "outgoing";
$this_call->{'gravityfree'}[6] = $event->{'CallerIDNum'};
$this_call->{'gravityfree'}[5] = $event->{'CallerIDName'};
$call{$unique_id} = $this_call;
} elsif ( length($event->{'CallerIDNum'}) == length($event->{'Exten'}) ) {
# Call is station to station
$this_call->{'status'} = "station-to-station";
}
}
}
}
}
sub newexten {
my ($ami, $event) = #_;
my $unique_id = $event->{'Uniqueid'};
my $this_call = $call{$unique_id};
# Handles inbound calls only
if ( defined $this_call->{'status'} && $this_call->{'status'} ne "station-to-station" ) {
# Call is not station to station
# Check if the DID has been defined
if ( not defined $this_call->{'gravityfree'}[13] ) {
if ( $event->{'Context'} eq 'ext-group' ) {
# Data required for Gravity Free
$this_call->{'gravityfree'}[13] = $event->{'Extension'};
}
}
}
}
sub dialcheck {
my ($ami, $event) = #_;
my $unique_id = $event->{UniqueID};
if ( exists $call{$unique_id} ) {
my $this_call = $call{$unique_id};
if ( defined $this_call->{'status'} && $this_call->{'status'} ne "station-to-station" ) {
# Call is not station to station
if ( $event->{'SubEvent'} eq 'Begin' && $this_call->{'gravityfree'}[3] =~ "incoming" ) {
# Call is inbound
$this_call->{'system_extension'} = $event->{'Dialstring'};
$this_call->{'dest_uniqueid'} = $event->{'DestUniqueID'};
# Data required for Gravity Free
$this_call->{'gravityfree'}[4] = $1 if $event->{'Channel'} =~ /(.+(?=\-\w+)).*/;
# Telnet data to Prodigy
my $sending = "R|$this_call->{'caller_name'}|$this_call->{'caller_number'}|$this_call->{'system_extension'}||$this_call->{'dnis'}|";
send_pos($sending,$t);
$this_call->{'status'} = "ringing";
} elsif ( $event->{SubEvent} eq 'Begin' && $this_call->{'gravityfree'}[3] =~ "outgoing" ) {
# Call is outbound
# Data required for Gravity Free
$this_call->{'gravityfree'}[4] = $1 if $event->{'Destination'} =~ /(.+(?=\-\w+)).*/;
$this_call->{'gravityfree'}[10] = $event->{'Destination'};
$this_call->{'gravityfree'}[7] = $event->{'Channel'};
}
}
}
}
sub outring {
my ($ami, $event) = #_;
my $unique_id = $event->{'Uniqueid'};
my $this_call = $call{$unique_id};
if ( defined $this_call->{'status'} && $this_call->{'status'} ne "station-to-station" ) {
# Call is not station to station
if ( not defined $this_call->{'holdstart'} && $this_call->{'gravityfree'}[3] eq "outgoing" ) {
# Call is outbound
$this_call->{'holdstart'} = time();
}
}
}
sub bridgecheck {
my ($ami, $event) = #_;
my $unique_id = $event->{'Uniqueid1'};
my $this_call = $call{$unique_id};
if ( defined $this_call->{'status'} && $this_call->{'status'} ne "station-to-station" ) {
# Call is not station to station
if ( $event->{'Bridgestate'} eq "Link" && length($event->{'CallerID2'}) <= 4 ) {
# Call is inbound
$this_call->{'dest_uniqueid'} = $event->{Uniqueid2};
# Data required for Gravity Free
$this_call->{'gravityfree'}[1] = time();
$this_call->{'gravityfree'}[10] = $event->{Channel2};
my $sending = "A|$this_call->{caller_name}|$this_call->{caller_number}|$event->{CallerID2}||$this_call->{dnis}|";
send_pos($sending,$t);
$this_call->{'status'} = "answered";
} elsif ( $event->{'Bridgestate'} eq "Link" && length($event->{'CallerID2'}) >= 4 ) {
# Call is outbound
$this_call->{'gravityfree'}[1] = time();
$this_call->{'gravityfree'}[13] = $this_call->{'gravityfree'}[1]-$this_call->{holdstart};
}
}
}
sub hangup {
my ($ami, $event) = #_;
my $unique_id = $event->{'Uniqueid'};
my $this_call = $call{$unique_id};
if ( defined $this_call->{'status'} && not defined $this_call->{'gravityfree'}[16] && $this_call->{'status'} ne "station-to-station" ) {
# Call is not station to station
if ( $event->{'Cause-txt'} eq "Normal Clearing" ) {
# Call was hungup normally
$this_call->{'dest_uniqueid'} = $event->{Uniqueid};
# Call has ended, get date/time
$this_call->{'gravityfree'}[2] = time();
# Mark call 'completed'
$this_call->{'gravityfree'}[16] = 1;
# Set notes to nothing
$this_call->{'gravityfree'}[17] = 'nada';
if ( defined $this_call->{'gravityfree'}[3] && $this_call->{'gravityfree'}[3] eq "incoming") {
# Call was inbound
if ( defined $this_call->{'gravityfree'}[1] ) {
# Call was answered
$this_call->{'gravityfree'}[13] = $this_call->{'gravityfree'}[1]-$this_call->{holdstart};
$this_call->{'gravityfree'}[14] = "Answered";
$this_call->{'gravityfree'}[15] = $this_call->{'gravityfree'}[2]-$this_call->{'gravityfree'}[1];
$this_call->{'gravityfree'}[8] = $event->{'ConnectedLineName'};
$this_call->{'gravityfree'}[9] = substr $event->{'ConnectedLineNum'}, -10;
# POST data to gravity free
send_http(\$this_call->{'gravityfree'});
} else {
# Call was abandoned
$this_call->{'gravityfree'}[14] = "Abandoned";
$this_call->{'gravityfree'}[13] = $this_call->{'gravityfree'}[2]-$this_call->{holdstart};
$this_call->{'gravityfree'}[15] = 0;
# POST data to gravity free
send_http(\$this_call->{'gravityfree'});
}
} elsif ( defined $this_call->{'gravityfree'}[3] && $this_call->{'gravityfree'}[3] eq "outgoing" ) {
# Call is outbound
if ( defined $this_call->{'gravityfree'}[1] ) {
# Call was bridged at some point
$this_call->{'gravityfree'}[15] = $this_call->{'gravityfree'}[2]-$this_call->{'gravityfree'}[1];
$this_call->{'gravityfree'}[14] = "Answered";
# POST data to gravity free
send_http(\$this_call->{'gravityfree'});
} else {
# Call was hung up before anyone answered
$this_call->{'gravityfree'}[15] = 0;
$this_call->{'gravityfree'}[14] = "Abandoned";
$this_call->{'gravityfree'}[13] = $this_call->{'gravityfree'}[2]-$this_call->{holdstart};
# POST data to gravity free
send_http(\$this_call->{'gravityfree'});
}
}
}
}
}
EV::loop
First question, where are you getting the array you're passing into the subroutine?
I ask because your example array is actually an array reference.
That is:
#array = (1, 2, 3); # This is an array
$ref = [1, 2, 3]; # This is an array reference
If you want to pass an array reference to your subroutine, change the beginning to:
sub send_http {
my $http = shift;
Next, let us consider how to iterate over the elements of an array. This is the proper way to do so:
foreach my $element ( #array ) {
# do stuff ...
}
When you do \# on an array, you are actually creating a reference to the array. Thus, if you truly are passing an array to your subroutine, you should change your loop to the following:
foreach my $v ( #http ) {
However, if you decide to pass your array as a reference, you can dereference the pointer and iterate over its elements like this:
foreach my $v ( #$http ) {
Hope this helps!
EDIT: For the newly uploaded code...
You're very close but we have a couple of small issues:
$this_call->{'gravityfree'} is actually already an array reference, I'm not certain why it's allowing you to address the array elements with $this_call->{'gravityfree'}[INDEX], so perhaps someone more knowledgeable than me can enlighten us all. I will note that the correct way to deference an array is the following:
\#{$this_call->{'gravityfree'}}
Regardless, you can simply pass the reference to your subroutine, no need to create a reference. That is:
send_http($this_call->{'gravityfree'});
Now, inside of your subroutine, you have an array reference. You are correctly reading the subroutine argument, but you need to dereference the reference in your foreach loop. Like so:
foreach my $v ( #$http ) {
# ... loop body
}
Does this make sense? Please let me know if anything is unclear (or not working!)
I'm not entirely sure what you're trying to do but everything depends on how you pass the array to your subroutine. You have two choices, either pass it as an array:
send_http(#array)
or as a reference to an array:
send_http(\#array)
As others have noted, your array is already a reference since you're defining it in square brackets [ ].
Which one you want will depend on what exactly you're doing but the syntax is different. To pass an array and iterate through it:
sub send_http {
my #http = #_;
foreach my $v (#http) {
print "v is $v\n";
}
}
my #aa=("cc","dd");
send_http(#aa);
To pass a reference and iterate through the array it points to:
sub send_http {
## Remove the first value from the #_ array.
my $http = shift #_;
## Dereference it to an array. You could also use #{$http}
foreach my $v (#$http) {
print "v is $v\n";
}
}
my #aa=("cc","dd");
send_http(\#aa);
The main difference is that when you use send_http(\#aa); what you're passing is not an array so you can't treat it as one. It is instead a reference to an array. Something like
send_http(ARRAY(0x1d34030));
So, the contents of #_ are just a single reference, ARRAY(0x1d34030). To treat it as an array, you need to dereference it to get to what it points to.
So, into your send_http, the argument you received is array_ref which is scalar variable, so when you used it you need to dereference to the right type.
NB: square bracket is array_reference
So, please change as below:
my $http = shift;
And please use that as :
foreach my $v ( #$http ) {
Example:
my $array_ref = [1,2,3];
print "Reference: ", $array_ref,"\n";
print "Array: ", #$array_ref,"\n";
Output:
Reference: ARRAY(0x7f8e1c004ee8)
Array: 123