how to combine two next if together - perl

I have a script that run on the list of files to do some of the changes, each file of them has a call event and the call event details contains 4 elements so i just want to do the changes on 2 of them only here I'm stuck with how to combine two next if in one loop, here I have used 2 loops to do the job but it takes more time , is there any idea about how to do that ?
my $calleventtag = $struct->{'transferBatch'}->{'callEventDetails'};
my #indexes = reverse (grep { exists $calleventtag->[$_]->{'supplServiceEvent'} } 0..$#$calleventtag);
my $sup_event_cnt = $#indexes;
foreach my $index (#indexes)
{
splice (#$calleventtag , $index,1);
}
foreach (0..$#$calleventtag)
{
next if ( ! exists $calleventtag->[$_]->{'mobileOriginatedCall'}) ;
if ( exists $calleventtag->[$_]->{'mobileOriginatedCall'}->{'basicCallInformation'}->{'destinationNetwork'} )
{
delete $calleventtag->[$_]->{'mobileOriginatedCall'}->{'basicCallInformation'}->{'destinationNetwork'};
}
if ( exists $calleventtag->[$_]->{'mobileOriginatedCall'}->{'basicCallInformation'}->{'chargeableSubscriber'}->{'simChargeableSubscriber'}->{'msisdn'}
&& $calleventtag->[$_]->{'mobileOriginatedCall'}->{'basicCallInformation'}->{'chargeableSubscriber'}->{'simChargeableSubscriber'}->{'msisdn'} !~ m/^96279/
)
{
delete $calleventtag->[$_]->{'mobileOriginatedCall'}->{'basicCallInformation'}->{'chargeableSubscriber'}->{'simChargeableSubscriber'}->{'msisdn'};
}
}
foreach (0..$#$calleventtag)
{
next if ( ! exists $calleventtag->[$_]->{'gprsCall'});
if ( exists $calleventtag->[$_]->{'gprsCall'}->{'gprsBasicCallInformation'}->{'gprsDestination'}->{'accessPointNameOI'} )
{
delete $calleventtag->[$_]->{'gprsCall'}->{'gprsBasicCallInformation'}->{'gprsDestination'}->{'accessPointNameOI'};
}
}

for (...) {
next if ...;
...
}
can also be written as
for (...) {
if (!...) {
...
}
}
You could use the following:
use Data::Diver qw( Dive );
my $call_event_details = Dive($struct, qw( transferBatch callEventDetails ));
for my $call_event_detail (#$call_event_details) {
next if !$call_event_detail->{supplServiceEvent};
if ( my $bci = Dive($call_event_detail, qw( mobileOriginatedCall basicCallInformation )) ) {
delete $bci->{destinationNetwork};
if ( my $scs = $bci->{simChargeableSubscriber} ) {
my $msisdc = $scs->{msisdn};
delete $scs->{msisdn} if $msisdc && $msisdc !~ /^96279/;
}
}
if ( my $dest = Dive($call_event_detail, qw( gprsCall gprsBasicCallInformation gprsDestination )) ) {
delete $dest->{accessPointNameOI};
}
}
Notes:
The quotes around string literals aren't needed in hash indexes if the string is valid valid identifier. For example, $hash->{'foo'} can be written as $hash->{foo}.
-> isn't needed between two indexes. For example, $hash->{foo}->{bar} can be written as $hash->{foo}{bar}.
If a hash element is either a reference or doesn't exist, you don't need to use exists to check if you have a reference; you can use a simple truth test since references are always true.
[BUG FIX] $hash->{foo}{bar} can autovivify $hash->{foo} (cause a reference to be assigned to it), so your tests to check if stuff exists could actually be causing things to be created. To fix this, you can replace
if ($hash->{foo}{bar})
with
if ($hash->{foo} && $hash->{foo}{bar})
or
if (Dive($hash, qw( foo bar )))
Using the same long chain of indexes (->{foo}{bar}{baz}) repeatedly is error prone.
It's best to use plural names for arrays. First, it's more descriptive, but it also makes choosing names for loop variables easier.
Speaking of variable names, why would use $calleventtag for the name of the variable containing callEventDetails nodes?
You don't need to check if a hash element exists before trying to delete it; delete can be passed an element that doesn't exist.
No need to loop over the indexes of an array if you don't need the indexes.
grep was a good choice, but splice was not. You should have used: $calleventtag = [ grep { ... } #$calleventtag ];. I moved the check into the loop.

Related

perl hash ref return: { 'a' =>1, % { sub() } } [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
{ 'a' =>1, % { sub() } }
Is this best for including a hash returned as ref into other?
%$sub() does not work.
As far I new {} makes new hash and returns reference, probably new optimized away this case, but not sure.
I'm guessing that you have some subroutine that returns a hash reference, and that you want to include all of those keys in a larger hash:
my $hash_ref = some_sub(...);
my $big_hash = { a => 1 };
There are a couple of ways that you can do. In you question, it looks like you are trying to do it inline by dereferencing the return value. That can be reasonable. The circumfix notation or postfix dereference can do this:
# general circumfix (old way)
my $big_hash = { a => 1, %{ some_sub() } };
# postfix dereference (new v5.24 hotness)
my $big_hash = { a => 1, some_sub()->%* };
However, I tend to not like to do this so directly. Typically I'm doing this when there are default values that might be overridden by whatever some_sub() is:
my %defaults = ( ... );
my %big_hash = ( %defaults, some_sub()->%* );
But, I usually go a step further by making the thing that produces the defaults into another subroutine so I can give subclassers a way to override it:
sub defaults {
my %defaults = ( ... );
return \%defaults;
}
my %big_hash = ( defaults()->%*, some_sub()->%* );
There are many other ways to merge hashes. There's How can I combine hashes in Perl? on StackOverflow, but also How do I merge two hashes in perlfaq4.
But, there's another thing to consider. Simply mashing two hashes together to get a new one might be expensive. What if the first hash is very, very large?
my %grand_hash = ( %very_big_hash, %new_hash );
People often do this and assign back to the starting hash, mostly because it's easy to type:
my %grand_hash = ( %grand_hash, %new_hash );
You're telling Perl to unroll %grand_hash, combine another list with it, then re-hash the huge list.
Although a bit more unwieldy, a better way is to add the new keys and values.
foreach my $new_key ( keys %new_hash ) {
$grand_hash{$new_key} = $new_hash{$new_key};
}
That's nice when you need to do something else, such as skipping keys that you already have in the hash:
foreach my $new_key ( keys %new_hash ) {
next if exists $grand_hash{$new_key};
$grand_hash{$new_key} = $new_hash{$new_key};
}
or maybe adding to a value that is already there instead of replacing it:
foreach my $new_key ( keys %new_hash ) {
$grand_hash{$new_key} += $new_hash{$new_key};
}
If you just need to add it and don't care about replacing values, a hash slice is nice for assigning multiple keys and values at the same time:
#grand_hash{ keys %new_hash } = values %new_hash;
In your case, you'd call the subroutine once and store the result so you don't have to construct the hash again. You then dereference that hash is the slice (or wherever you want to use it:
my $new_hash = some_sub(...);
#grand_hash{ keys %$new_hash } = values %$new_hash;
Yes, to dereference a hash reference and get a list of key/value pairs, wrap %{ ... } around the expression that generates the hash reference.
Or, since perl 5.24, you can use the postfix dereference syntax sub()->%*

Iterating the results returned from fetchall_arrayref

I have a sql wherein I am fetching few records, sorted by full name.
My requirement is to extract chunks of similar names and then do some operation on it.
Say, the sql returns some records containing names like [name1,name1,name2,name3,name3]
I need to split them to [name1,name1] , [name2] , [name3,name3]
I am able to do it, but I am not happy with my implementation as I have to call doSomethingWithNames()twice.
while (my $paRecs = $pStatementHandle->fetchall_arrayref({}, 3)){
foreach my $phRec(#{$paRecs}){
my $pCurrentName = $phRec->{'FULL_NAME'};
if ((scalar(#aParentRecords) eq 0) or ($aParentRecords[-1] eq $pCurrentName)){
push(#aParentRecords, $pCurrentName);
} else {
doSomethingWithNames(\#aParentRecords);
#aParentRecords= ();
push(#aParentRecords, $pCurrentName);
}
}
};
doSomethingWithNames(\#aParentRecords); # This should be inside while loop
I believe am running into this issue because while doesn't go into the loop for
the last iteration as fetch* returns undef.
Sounds basic PERL stuff, but tried many loop constructs with no luck.
Any pointer will be a great help
The trick is to postpone existing the loop by converting it into an infinite loop. This requires checking the loop-terminating condition (!$rows) twice, though.
my $rows = [];
my $prev_name = '';
my #group;
while (1) {
$rows = $sth->fetchall_arrayref({}, 3) if !#$rows;
if (!$rows || $rows->[0]->{FULL_NAME} ne $prev_name)
if (#group) {
do_something(\#group);
#group = ();
}
last if !$rows;
}
$prev_name = $rows->[0]->{FULL_NAME};
push #group, shift(#$rows);
}

how to store and retrieve perl objects

Problem : I want to have a list of objects stored so that i can call the corresponding methods at latter point in time
my #tc = ("TC_1","TC_2");
my %obj_list = ();
foreach my $test (#tc) {
$obj_list{$test} = Test->new($test);
}
In the same module file at latter stage where i need to call the corresponding methods of those objects
foreach my $test (keys %obj_list) {
if (some specific condition is satisfied for a test) {
1 --> $obj_list->$test->action();
2 --> $obj_list{$test}->action();
}
}
I tried 1 and 2 and they are not working. Could some one tell me what i could be doing wrong here.Any inputs would be of great help.
Your code is basically correct - other than a few syntax errors.
# Use ( ... ) to initialise an array.
my #tc = ("TC_1","TC_2");
my %obj_list = ();
foreach my $test (#tc) {
$obj_list{$test} = Test->new($test);
}
foreach (keys %obj_list) {
if (some specific condition is satisfied for a test) {
# This version is incorrect
# $obj_list->$key->action();
# This version will work, except you have the
# key in $_, not $key.
$obj_list{$_}->action();
}
}
Adding use strict and use warnings to your code would have helped you find some of these problems.

Pushing a hash to an existing hash in Perl

I'm new to Perl, so you'll have to forgive my code.
I'm reading a file that tree structured (like xml, just not actually) and I want to foreach through the tree and if a certain "node" doesn't have a child node, I want to insert it. Simple enough.
Here's my code:
foreach $key ( #{$struct->{'transferBatch'}->{'callEventDetails'} } ) {
foreach ( keys %{$key} ) {
if ( $_ eq "mobileTerminatedCall" ) {
if ( defined $key->{$_}->{'basicServiceUsedList'} ) {
if ( defined $key->{$_}->{'basicServiceUsedList'}[0]->{'chargeInformationList'} ) {
if ( not defined $key->{$_}->{'basicServiceUsedList'}[0]->{'chargeInformationList'}[0]->{'CallTypeGroup'} ) {
$CallTypeGroup = {
"CallTypeLevel1:" => "0",
"CallTypeLevel2:" => "0",
"CallTypeLevel3:" => "0"
};
#Doesn't work!
$key->{$_}->{'basicServiceUsedList'}[0]->{'chargeInformationList'}[0]{'CallTypeGroup'} = $CallTypeGroup;
}
}
}
}
}
}
The iteration is working fine, but my push call fails saying it's not an ARRAY reference. I feel like I'm close, but I need that line to insert the $CallTypeGroup hash as a child to the current spot.
Any help is appreciated!
$key->{$_}->{'basicServiceUsedList'}[0]->{'chargeInformationList'}[0]
contains a reference to a hash, as created here (if not earlier):
if ( not defined
$key->{$_}{basicServiceUsedList}[0]
->{chargeInformationList}[0]
->{CallTypeGroup} )
You did not indicate what data structure you want, so I'm not sure how we can help other than explain the message. I think you simply want to change that if to
if ( not defined
$key->{$_}{basicServiceUsedList}[0]
->{chargeInformationList}[0] )
btw, you really should use variables to hold the intermediate references instead of having such long "names".
btw, I'm highly skeptical of all those hardcoded zero indexes.

Perl hash key determined by array

I have an array and I am making a hash instance from it.
For instance, if array is:
#folders=(temp,usr,bin);
then i want to fill in hash:
$the_path{$folders[0]}{$folders[1]}{$folders[2]}="somevalue";
But if the array is only:
#folders=(bin);
then i want the path to be:
$the_path{$folders[0]}="somevalue";
The problem is I dont know beforehand how long the array is gonna be, and I would really like to avoid making x if statements for that solution scales terribly.
How do I do this?
First, that's not how you define an array in Perl. You probably want to say
my #folders = ( 'temp', 'usr', 'bin' );
There's an old trick for making nested hash keys from a list:
my %the_path;
my $tmp = \%the_path;
foreach my $item( #folders ) {
$tmp->{$item} = { };
$tmp = $tmp->{$item};
}
This will result in a structure like the following:
$VAR1 = {
'temp' => {
'usr' => {
'bin' => {}
}
}
};
If you want to replace the empty hashref at the bottom-most level with a string, you can keep track of a count variable inside the loop.