perl assign variables to an array with relationship - perl

Please advice how to pass 3 variables in an array with relation.
#item = ($a , $b , $c);
#record = push(#array, #item);
I want to assign value in a #array so that if I look for any instance I should get value of all a,b,c.
Is there any way apart from comma to assign a value in array. like $a:$b:$c or $a>$b>$c
I need this because i am want to grep 1 record(a) and get (a:b:c)
#array1 = grep(!/$a/, #array);
expected output should be a:b:c
Thanks,

The question is not very clear. Maybe you should rephrase it.
However, I understand you want an array with groups of three elements.
You might want to use array references.
#item = ($a , $b , $c);
push(#array, \#item);
or
$item = [$a , $b , $c];
push(#array, $item);
Also, pushwon't return an array as you expect. Perldoc says:
Returns the number of elements in the array following the completed
"push".
Now if you want to filter these groups of three elements, you can do something like that:
my #output = ();
L1: foreach ( #array ){
L2: foreach( #$_ ){
next L1 if $_ eq $a;
}
push #output, $_;
}
Please note that if you want an exact match you should use the eq operator instead of a regex...

Related

Why am I getting no output from this short Perl script?

Why isn't the below code returning any output? I am expecting it to print out "Contains".
sub is_member {
my ($x, #list) = #_;
foreach (#list) {
if ($x eq $_) {"contains"} else {"doesn't contain"}
}
}
my #a = qw(apple x orange but so nos how dod to it a b c);
print is_member("apple",#a)."\n";
Like below code just run fine, outputting "is palindrome"
sub is_palindrome {
my ($x) = #_;
if ($x eq reverse($x)){"is palindrome"} else {"Not a palindrome"}
}
print is_palindrome("radar")."\n";
This code:
if ($x eq $_) {"contains"} else {"doesn't contain"}
does not print or return anything. It evaluates two constants, but that's all. I'm surprised there isn't a complaint about missing semicolons, too. And the indentation is eccentric.
You probably want something more like:
sub is_member
{
my ($x, #list) = #_;
foreach (#list)
{
return "contains" if ($x eq $_);
}
return "doesn't contain";
}
for loops do not have return values. In is_palindrome, the if statement is evaluated by itself which produces the return value for the subroutine implicitly.
In the case of the for loop, even if the last statement evaluated inside a for loop became the return value, the last comparison made, "apple" eq "c", would be false.
If you want to do the test using a for loop, you will need to exit the loop early. But, in Perl, grep is the built-in way to test if the elements of a list satisfy a condition.
my $result = (grep $_ eq $x, #list) ? "contains" : "does not contain";
print "'#list' $result $x\n";
If you are only interested in the existence of a certain element, List::MoreUtils::any
and List::MoreUtils::first_index provide performance advantages in case the list is long.
The conditional operator, $cond ? $t : $f, is more useful for writing compact conditionals than trying to fit an if statement on a single line with braces and all.
to clarify on the return values of subs, by default, Perl returns the value of the evaluation of the last expression in the sub... unless it's in a loop. from perldoc:
If no return is found and if the last statement is an expression, its
value is returned. If the last statement is a loop control structure
like a foreach or a while , the returned value is unspecified. The
empty sub returns the empty list.
but as others have stated, you should always use return if you are returning anything, for both readability and stability.
it's also true that your sub is unnecessary. grep will return 0 or 1 in a scalar context (that you can use as boolean), or a new array of matching values in an array context; ex:
my #a = qw(apple x orange but so nos how dod to it a b c);
print "found" if grep /^orange$/, #a; # prints "found"
my #b = grep length > 3, #a;
print "#b" # prints "apple orange"

Selecting highest count of element except when...

So i have been working on this perl script that will analyze and count the same letters in different line spaces. I have implemented the count to a hash but am having trouble excluding a " - " character from the output results of this hash. I tried using delete command or next if, but am not getting rid of the - count in the output.
So with this input:
#extract = ------------------------------------------------------------------MGG-------------------------------------------------------------------------------------
And following code:
#Count selected amino acids.
my %counter = ();
foreach my $extract(#extract) {
#next if $_ =~ /\-/; #This line code does not function correctly.
$counter{$_}++;
}
sub largest_value_mem (\%) {
my $counter = shift;
my ($key, #keys) = keys %$counter;
my ($big, #vals) = values %$counter;
for (0 .. $#keys) {
if ($vals[$_] > $big) {
$big = $vals[$_];
$key = $keys[$_];
}
}
$key
}
I expect the most common element to be G, same as the output. If there is a tie in the elements, say G = M, if there is a way to display both in that would be great but not necessary. Any tips on how to delete or remove the '-' is much appreciated. I am slowly learning perl language.
Please let me know if what I am asking is not clear or if more information is needed, thanks again kindly for all the comments.
Your data doesn't entirely make sense, since it's not actually working perl code. I'm guessing that it's a string divided into characters. After that it sounds like you just want to be able to find the highest frequency character, which is essentially just a sort by descending count.
Therefore the following demonstrates how to count your characters and then sort the results:
use strict;
use warnings;
my $str = '------------------------------------------------------------------MGG-------------------------------------------------------------------------------------';
my #chars = split '', $str;
#Count Characteres
my %count;
$count{$_}++ for #chars;
delete $count{'-'}; # Don't count -
# Sort keys by count descending
my #keys = sort {$count{$b} <=> $count{$a}} keys %count;
for my $key (#keys) {
print "$key $count{$key}\n";
}
Outputs:
G 2
M 1
foreach my $extract(#extract) {
#next if $_ =~ /\-/
$_ setting is suppressed by $extract here.
(In this case, $_ keeps value from above, e.g. routine argument list, previous match, etc.)
Also, you can use character class for better readability:
next if $extract=~/[-]/;

How do I refresh an array in a foreach loop?

I am writing a Perl script to do some mathematical operations on a hash. This hash contains the values as given in the sample below. I have written the code below. If I execute this code for an array value separately without using a foreach loop, the output is fine. But if I run this using a foreach loop on the array values, the sum for values in A are good, but from B the output add the previous values.
Hash Sample:
$VAR1 = 'A';
$VAR2 = {
'"x"' => [values],
'"y"' => [values],
and so on...
$VAR3 = 'B';
$VAR4 = {
'"x"' => [values],
'"y"' => [values],
and so on...
$VARn....
Code:
#!/usr/bin/perl -w
use strict;
use List::Util qw(sum);
my #data;
my #count;
my $total;
my #array = ("A", "B", "C", "D");
foreach my $v (#array) {
my %table = getV($v); #getV is a subroutine returing a hash.
for my $h (sort keys %table) {
for my $et (sort keys %{ $table{$h} } ) {
for $ec ($table{$h}{$et}) {
push #data, $ec;
#count = map { sum(#{$_}) } #data;
$total = sum(#count);
}
}
print "sum of $v is $total\n";
}
I think the issue is with this line. It is storing all the previous values and hence adding all the values in next foreach loop.
push #data, $ec;
So, here I have two issues:
1) How can I refresh the array (#data) in each foreach loop iteration?
2) How can I add the values in the array ref ($ec) and store them in an array? Because when I use the following code:
for $ec ($table{$h}{$et}) {
#count = map { sum(#{$_}) } #$ec;
$total = sum(#count);
}
The output gives me the same values for #count and $total.
Please provide me with suggestions.
If I understand you correctly, just a small change in your code. Make an empty array (#data) at the beginning of for loop. Hope this helps.
for my $h (sort keys %table) {
my #data;
1) Declare the #data array at the top of the loop body where you want to start with a fresh, empty array. Or maybe you mean to be saying #data = #$ec, not push #data, $ec?
2) To add the values in the array referred to by $ec, you would just say sum(#$ec); no map required.
It's not completely clear what your data structure is or what you are trying to do with it.
It would help to see what a sample %table looks like and what results you expect from it.

How can I compare different elements of array in Perl?

I am new to this field. So kindly go easy on me. I have two arrays:
#array1 = ("ABC321", "CDB672", "PLE89",....);
#array2 = ("PLE89", "ABC678", "LMD789",...);
I want to compare elements of these two different arrays. But, I want to only match letters with letters. So for instance, if arrays are compared, $array[2] element (PLE) should match with $array2[0] (PLE) and similarly $array1[0] (ABC) should match with $array[1] (ABC). I am able to do it one at time but not able to compare all elements of both array at the same time (that is looping the arrays).
my ($value1)= ($array[2]=~ /([A-Z]+)[0-9]+/);
print "Value1: $value1 \n";
my ($value2)= ($array[0]=~ /([A-Z]+)[0-9]+/);
print "Value2 : $value2 \n";
if ($value1 eq $value2){
print " length \n";
}
Any suggestions on how to do I set up loop for both arrays at the same time?
You can use a hash as a lookup device and get an O(m+n) solution (where m is the length of array1 and n is the length of array2).
#!/usr/bin/perl
use strict;
use warnings;
my #array1 = qw(ABC321 CDB672 PLE89);
my #array2 = qw(PLE89 ABC678 LMD789);
my %seen;
for my $item (#array1) {
die "not a valid item: $item"
unless my ($key) = $item =~ /([A-Z]+)/;
#we are using an array to hold the items in case
#the same key shows up more than once in an array
#this code can be simpler if you can guarantee
#that the keys are unique
push #{$seen{$key}}, $item;
}
for my $item (#array2) {
die "not a valid item: $item"
unless my ($key) = $item =~ /([A-Z]+)/;
if (exists $seen{$key}) {
print "$item is in array1, it matches #{$seen{$key}}\n";
} else {
print "$item is not in array1\n";
}
}
Language-agnostic suggestion would be to sort both arrays first (should take you O(n lg(n)), then compare with two iterators in linear time.
If performance is not an issue, just keep it simple and go with quadratic number of pair-wise comparisons.
While sorting you can also get rid of digits in the end.

perl: getting value out of a hash using map

It seems like I should be able to do this with map, but the actual details elude me.
I have a list of strings in an array, and either zero or one of them may have a hash value.
So instead of doing:
foreach $str ( #strings ) {
$val = $hash{$str} if $hash{$str};
}
Can this be replaced with a one-liner using map?
#values = grep { $_ } #hash{#strings};
to account for the fact that you only want true values.
Change this to
#values = grep { defined } #hash{#strings};
if you want to skip undefined values.
Sure, it'd be:
map { $val = $hash{$_} } #strings;
That is, each value of #strings is set in $_ in turn (instead of $str as in your foreach).
Of course, this doesn't do much, since you're not doing anything with the value of $val in your loop, and we aren't capturing the list returned by map.
If you're just trying to generate a list of values, that'd be:
#values = map { $hash{$_} } #strings;
But it's more concise to use a hash slice:
#values = #hash{#strings};
EDIT: As pointed out in the comments, if it's possible that #strings contains values that aren't keys in your hash, then #values will get undefs in those positions. If that's not what you want, see Hynek's answer for a solution.
I'm used to do it in this way:
#values = map { exists $hash{$_} ? $hash{$_} : () } #strings;
but I don't see anything wrong in this way
push #values, $hash{$_} for grep exists $hash{$_}, #strings;
or
#values = #hash{grep exists $hash{$_}, #strings};
map { defined $hash{$_} && ( $val = $hash{$_})} #strings;