I have a hash %m_h with a couple of different data types inside. I want to remove the item 'q20_bases' from the array in $VAR4 but can't figure out how.
Data Structure (From print Dumper %m_h)
$VAR1 = 'run_m';
$VAR2 = [
'run_id',
'machine',
'raw_clusters',
'passed_filter_reads',
'yield'
];
$VAR3 = 'ln_m';
$VAR4 = [
'run_id',
'lane_number',
'read_number',
'length',
'passed_filter_reads',
'percent_passed_filter_clusters',
'q20_bases',
'q30_bases',
'yield',
'raw_clusters',
'raw_clusters_sd',
'passed_filter_clusters_per_tile',
'passed_filter_clusters_per_tile_sd',
'percent_align',
'percent_align_sd'
];
I tried delete $m_h{'q20_bases'}; though it did nothing and I'm not sure what direction to head in.
delete removes a key and the associated value from a hash, not an element from an array.
You can use grep to select the elements of the array that are different to q20_bases.
$m_h{ln_m} = [grep $_ ne 'q20_bases', #{ $m_h{ln_m} }];
or
#{ $m_h{ln_m} } = grep $_ ne 'q20_bases', #{ $m_h{ln_m} };
You can also use splice to remove an element from an array, but you need to know its index:
my ($i) = grep $m_h{ln_m}[$_] eq 'q20_bases', 0 .. $#{ $m_h{ln_m} };
splice #{ $m_h{ln_m} }, $i, 1;
You can see that you always need to dereference the value with #{...} to get the array from the array reference. Recent Perls also provide an alternative syntax for it:
$m_h{ln_m}->#*
Related
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...
I have an array of array that looks like this -
$VAR1 = [
'sid_R.ba',
'PS20TGB2YM13',
'SID_r.BA',
'ARS',
'XBUE'
]; $VAR2 = [
'sddff.pk',
'PQ10XD06K800',
'SDDFF.PK',
'USD',
'PINX'
]; $VAR3 = [
'NULL',
'NULL',
'NULL',
'.',
'XNAS'
]; $VAR4 = [
'NULL',
'NULL',
'NULL',
'.',
'XNAS'
]; $VAR5 = [
'NULL',
'NULL',
'NULL',
'EUR',
'OTCX'
]; $VAR6 = [
'sid.ba',
'PS20TGB1TN17',
'SID.BA',
'ARS',
'XBUE'
];
I want to remove the complete block (array ref) if any of its element is NULL
I have a code in which the array gets generated, so I tried a for loop to delete but then the index of the array is reduced on the inside the for loop.
So I dont know in which order the array will be or the length of array.
Please I need a generic solution.
Please help.
Thanks
You seem to have an array like
my #AoA = (
[1, 2, 3],
[4, 5, 6],
[7, 8, "NULL"],
[9, 10],
);
You want to select all child arrays that do not contain "NULL". Easy: Just use nested grep:
my #AoA_sans_NULL = grep {
not grep { $_ eq "NULL" } #$_
} #AoA;
The grep { CONDITION } #array selects all elements from #array where the CONDITION evaluates to true.
The grep { $_ eq "NULL" } #$_ counts the number of "NULL"s in the inner array. If this is zero, our condition is true, else, we don't want to keep that sub-array.
use List::MoreUtils qw(none);
my #filtered = grep {
none { $_ eq "NULL" } #$_;
} #array;
Does this do what you want?
my #new_array = grep { scalar(grep { $_ eq 'NULL' } #{$_}) == 0 } #old_array;
Old school:
my #filtered = ();
ARRAY_LOOP:
for my $array ( #AoA ){
ITEM_LOOP:
for my $item ( #$array ){
next ARRAY_LOOP if $item eq 'NULL';
} # end ITEM_LOOP
push #filtered, $array;
} # end ARRAY_LOOP
This code will be slower than the others, but an in-place solution might be useful if the data-set is very large.
use List::MoreUtils qw(any);
for(my $i = 0; $i < #AoA; $i ++) {
splice #AoA, $i --, 1
if any { $_ eq "NULL" } #{ $AoA[$i] };
}
A non-grep of a grep solution:
my #array = ...; #Array of Arrays
for my $array_index ( reverse 0 .. $#array ) {
my #inner_array = #{ $array[$array_index] };
if ( grep /^NULL$/, #inner_array ) {
splice #array, $array_index, 1;
}
}
say Dumper #array;
The splice command removes the entire subarray. I don't need to create #inner_array I could have used my dereferenced #{ $array[$array_index] } in the if statement, but I like going for clarity.
The only gotcha is that you have to go through your array of array backwards. If you go through your array from first element to last element, you'll remove element 2 which causes all the other elements to have their indexes decremented. If I first remove element 4, element 0 to 3 don't change their index.
It's not as elegant as the grep of a grep solutions, but it's a lot easier to maintain. Imagine someone who has to go through your program six months from now trying to figure out what:
grep { not grep { $_ eq "NULL" } #$_ } #array;
is doing.
Below is my code(just playing with hashes) where I want to create a hash of array(keys assigning to array). But I get the output as array reference. Why is this array reference displaying?
#!/usr/bin/perl
my #result = (0,0,0);
my #operator = ('AP', 'MP', 'UP');
my %operator_res;
for ( $i = 0; $i <= $#operator; $i++ ) {
if ( $i == 2 ) {
#result = (4,5,6);
} elsif ( $i == 1 ) {
#result = (1,2,3);
}
#{$operator_res{$operator[$i]}} = #result;
}
foreach $keys (%operator_res) {
print "$keys:";
#print "#{$operator_res{$keys}}\n";
print "$operator_res{$keys}[0], $operator_res{$keys}[1], $operator_res{$keys}[2]\n";
}
Output is
UP:4, 5, 6
ARRAY(0x17212e70):, , Why is this array reference printing?
AP:0, 0, 0
ARRAY(0x17212e00):, ,
MP:1, 2, 3
ARRAY(0x17212e20):, ,
foreach $keys (%operator_res)
should be
foreach $keys (keys %operator_res)
Your foreach loop iterates over each element of %operator_res, not just over the keys. As ikagim already answered, you have to use keys to get only the keys of the hash.
If you have a look with Data::Dumper on the %operator_res the Output is:
$VAR1 = 'UP';
$VAR2 = [
4,
5,
6
];
$VAR3 = 'AP';
$VAR4 = [
0,
0,
0
];
$VAR5 = 'MP';
$VAR6 = [
1,
2,
3
];
As you see, you will always get two iterations per element: one for the key and one for the array ref.
A hash value in Perl must be a scalar. To simulate multidimensional hashes, use values that are references to hashes or arrays.
The line
#{$operator_res{$operator[$i]}} = #result;
in your question is equivalent to
$operator_res{ $operator[$i] } = [ #result ];
That is, the value associated with the key $operator[$i] at the time is a reference to a new array whose contents are the same as #result.
For many examples, read the perllol documentation.
You could use Data::Dumper to print out your data in a well formatted way:
use Data::Dumper;
print Dumper(\%operator_res);
Q: Why is this array reference printing?
A: Because of this line: print "$keys:";
I want to get the corresponding values from the second list based on the index value of the unique values in my first list using perl.
For ex:
#list1=('a','b','c','a','d');
#list2=('e','f','g','a','i');
i want to create two new list without the duplicate values
#new_list1=('a','b','c','d');
#new_list2=('e','f','g','i');
How can i do this?
to get unique values from one list i can use:
my %temp_hash = map { $_, 0 } #list1;
my #uniq_array = keys %temp_hash;
print "#uniq_array\n";
But how to get the values at the corresponding index from the other list.
Thanks in advance.
EDIT:
It should find unique values based on some condition and not the first occurrence.
for ex:
#list1=('a','b','c','a','d');
#list2=('e','f','g','a','i');
and
#list1=('a','b','c','a','d');
#list2=('a','f','g','e','i');
should give the same value:
#new_list1=('a','b','c','d');
#new_list2=('e','f','g','i');
In the given example the condition may be not to include the common element where the value for both the list are same. ie there are two occurrence of 'a' 1st 'a' corresponds to 'e' and 2nd 'a' corresponds to 'a'. so the second one is to be removed not the first one.
When dealing with parallel arrays, one deals with indexes. To find the list of indexes, one can start with the following common method of finding unique values:
my %seen;
my #uniq = grep !$seen{$_}++, #dups;
and expand it to take a list of indexes as input:
my %seen;
my #indexes = grep !$seen{ $list1[$_] }++, 0..$#list1;
In your updated question, you need something more complex:
my %options;
for (0..$#list1) {
push #{ $options{ $list1[$_] } }, $_;
}
my %seen;
my #indexes;
for (0..$#list1) {
next if $seen{$_}++;
my #options = #{ $options{ $list1[$_] } };
my $option = pick(\#list1, \#list2, \#options) // $options[0];
push #indexes, $option;
}
Then all you have to do is extract the desired elements:
my #new_list1 = #list1[ #indexes ];
my #new_list2 = #list2[ #indexes ];
The pick function for the possible selection algorithm you describe is:
sub pick {
my ($list1, $list2, $options) = #_;
return ( grep $list1->[$_] ne $list2->[$_], #options )[0];
}
use strict;
use warnings;
use Data::Dumper;
my #list1=('a','b','c','a','d');
my #list2=('e','f','g','a','i');
my %hash1 = ();
my %hash2 = ();
$hash1{$_}++ for #list1;
my #uniq_list1 = sort keys %hash1;
for (#list2)
{
next if defined ($hash1{$_});
$hash2{$_}++;
}
my #uniq_list2 = sort keys %hash2;
print Dumper(\#uniq_list1);
print Dumper(\#uniq_list2);
Output:
$VAR1 = [
'a',
'b',
'c',
'd'
];
$VAR1 = [
'e',
'f',
'g',
'i'
];
What is the best practise to solve this?
if (... )
{
push (#{$hash{'key'}}, #array ) ;
}
else
{
$hash{'key'} ="";
}
Is that bad practise for storing one element is array or one is just double quote in hash?
I'm not sure I understand your question, but I'll answer it literally as asked for now...
my #array = (1, 2, 3, 4);
my $arrayRef = \#array; # alternatively: my $arrayRef = [1, 2, 3, 4];
my %hash;
$hash{'key'} = $arrayRef; # or again: $hash{'key'} = [1, 2, 3, 4]; or $hash{'key'} = \#array;
The crux of the problem is that arrays or hashes take scalar values... so you need to take a reference to your array or hash and use that as the value.
See perlref and perlreftut for more information.
EDIT: Yes, you can add empty strings as values for some keys and references (to arrays or hashes, or even scalars, typeglobs/filehandles, or other scalars. Either way) for other keys. They're all still scalars.
You'll want to look at the ref function for figuring out how to disambiguate between the reference types and normal scalars.
It's probably simpler to use explicit array references:
my $arr_ref = \#array;
$hash{'key'} = $arr_ref;
Actually, doing the above and using push result in the same data structure:
my #array = qw/ one two three four five /;
my $arr_ref = \#array;
my %hash;
my %hash2;
$hash{'key'} = $arr_ref;
print Dumper \%hash;
push #{$hash2{'key'}}, #array;
print Dumper \%hash2;
This gives:
$VAR1 = {
'key' => [
'one',
'two',
'three',
'four',
'five'
]
};
$VAR1 = {
'key' => [
'one',
'two',
'three',
'four',
'five'
]
};
Using explicit array references uses fewer characters and is easier to read than the push #{$hash{'key'}}, #array construct, IMO.
Edit: For your else{} block, it's probably less than ideal to assign an empty string. It would be a lot easier to just skip the if-else construct and, later on when you're accessing values in the hash, to do a if( defined( $hash{'key'} ) ) check. That's a lot closer to standard Perl idiom, and you don't waste memory storing empty strings in your hash.
Instead, you'll have to use ref() to find out what kind of data you have in your value, and that is less clear than just doing a defined-ness check.
I'm not sure what your goal is, but there are several things to consider.
First, if you are going to store an array, do you want to store a reference to the original value or a copy of the original values? In either case, I prefer to avoid the dereferencing syntax and take references when I can:
$hash{key} = \#array; # just a reference
use Clone; # or a similar module
$hash{key} = clone( \#array );
Next, do you want to add to the values that exist already, even if it's a single value? If you are going to have array values, I'd make all the values arrays even if you have a single element. Then you don't have to decide what to do and you remove a special case:
$hash{key} = [] unless defined $hash{key};
push #{ $hash{key} }, #values;
That might be your "best practice" answer, which is often the technique that removes as many special cases and extra logic as possible. When I do this sort of thing in a module, I typically have a add_value method that encapsulates this magic where I don't have to see it or type it more than once.
If you already have a non-reference value in the hash key, that's easy to fix too:
if( defined $hash{key} and ! ref $hash{key} ) {
$hash{key} = [ $hash{key} ];
}
If you already have non-array reference values that you want to be in the array, you do something similar. Maybe you want an anonymous hash to be one of the array elements:
if( defined $hash{key} and ref $hash{key} eq ref {} ) {
$hash{key} = [ $hash{key} ];
}
Dealing with the revised notation:
if (... )
{
push (#{$hash{'key'}}, #array);
}
else
{
$hash{'key'} = "";
}
we can immediately tell that you are not following the standard advice that protects novices (and experts!) from their own mistakes. You're using a symbolic reference, which is not a good idea.
use strict;
use warnings;
my %hash = ( key => "value" );
my #array = ( 1, "abc", 2 );
my #value = ( 22, 23, 24 );
push(#{$hash{'key'}}, #array);
foreach my $key (sort keys %hash) { print "$key = $hash{$key}\n"; }
foreach my $value (#array) { print "array $value\n"; }
foreach my $value (#value) { print "value $value\n"; }
This does not run:
Can't use string ("value") as an ARRAY ref while "strict refs" in use at xx.pl line 8.
I'm not sure I can work out what you were trying to achieve. Even if you remove the 'use strict;' warning, the code shown does not detect a change from the push operation.
use warnings;
my %hash = ( key => "value" );
my #array = ( 1, "abc", 2 );
my #value = ( 22, 23, 24 );
push #{$hash{'key'}}, #array;
foreach my $key (sort keys %hash) { print "$key = $hash{$key}\n"; }
foreach my $value (#array) { print "array $value\n"; }
foreach my $value (#value) { print "value $value\n"; }
foreach my $value (#{$hash{'key'}}) { print "h_key $value\n"; }
push #value, #array;
foreach my $key (sort keys %hash) { print "$key = $hash{$key}\n"; }
foreach my $value (#array) { print "array $value\n"; }
foreach my $value (#value) { print "value $value\n"; }
Output:
key = value
array 1
array abc
array 2
value 22
value 23
value 24
h_key 1
h_key abc
h_key 2
key = value
array 1
array abc
array 2
value 22
value 23
value 24
value 1
value abc
value 2
I'm not sure what is going on there.
If your problem is how do you replace a empty string value you had stored before with an array onto which you can push your values, this might be the best way to do it:
if ( ... ) {
my $r = \$hash{ $key }; # $hash{ $key } autoviv-ed
$$r = [] unless ref $$r;
push #$$r, #values;
}
else {
$hash{ $key } = "";
}
I avoid multiple hash look-ups by saving a copy of the auto-vivified slot.
Note the code relies on a scalar or an array being the entire universe of things stored in %hash.