Pushing a list of key/value pairs in Perl Hash - perl

I'm trying to 'push' a list of key/value pairs into a perl hash as follows. I thought it would work like the key-value pairs are assigned when an array is assigned to a hash. #targets contains tab separated strings for each elements. However for every iteration of the map loop, the hash is overwitten and at the end I only got one key-value pair in the hash which corresponds to the last element of the #targets. I'm trying to avoid the usual $ID_Gene{$key}=$value type assignments.
How to push a list as a key-value pair to the hash?
Alternatively, is there any way I can build an anonymous hash and then push that hash to the original hash?Like: %ID_Gene = (%ID_Gene, %AnonyHash);
my %ID_Gene;
map{ %ID_Gene= (split /\t/,$_) ;
}#targets;

You're almost there, you just have the assignment in the wrong place, so you end up blowing away the entire hash each time you add an item. As a general rule, it usually doesn't make sense to do things with side-effects in map. You can simply do:
my %ID_Gene = map { split /\t/, $_ } #targets;
if you already have some stuff in %ID_Gene and you want to add to it, you can do
%ID_Gene = (%ID_Gene, map { split /\t/, $_ } #targets);
or if you think that's too much going on in one line:
my %to_add = map { split /\t/, $_ } #targets;
%ID_Gene = (%ID_Gene, %to_add);

Related

How to store values of hash in array in perl?

I have a hash having duplicate values and unique keys.I have to store keys in array of size 5, if more keys are there new array should be created and stored in it.
The keys stored in 1 array should have same value.
Note: I have to read those values from excel sheet and generate c source file.
Ex:
%hash = (a=>1,b=>2,c=>1,d=>1,e=>3,f=>4,g=>4,h=>1,i=>1,j=>1);
output in c file:
datatype arr1[]={a,c,d,h,i};
datatype arr2[]={j};
datatype arr3[]={b};
datatype arr4[]={e};
datatype arr5[]={f,g};
So you need to find keys that have the same values?
So we need to kind of revert the array, but being a bit smart to handle that the original values are not unique. Som instead of just transforming 'key' => 'value' pairs to 'value' => 'key', we need to store the keys in arrays.
my %hash = ...;
my %transposed;
for my $key (keys %hash) {
my $value = $hash{$key};
$transposed{$value} = [] unless defined $transposed{$value};
push #{ $transposed{$value} }, $key;
}
Then you have a hash of arrays, where each key is a value in the original hash and the elements of the arrays are the keys. The next step is to iterate over the keys and spilt each list into lines of 5 elements:
for my $key (sort keys %transposed) {
while (#{ $transposed{$key} }) {
my #list = splice #{ $transposed{$key} }, 0, 5;
say join ", ", #list;
}
}
The main parts is the while loop iterating as long as there are elements in the current list and the splice removes and returns up to 5 element from the list each iteration. Adding the exact C code is left as an exercise for the interested reader... :-)
You might need to read up on references: http://perldoc.perl.org/perlreftut.html
The line setting a hash value to a reference to an empty array is not necessary as perl will automatically create a arrayref when you tries to push a value to it. I have included it to make it clearer what is going on.

How to get a single key (random ok) from a very large hash in perl?

Suppose you have a very large hash (lots of keys), and have a function that potentially deletes many of those keys, e.g.:
while ( each %in ) {
push #out, $_;
functionThatDeletesOneOrMoreKeys($_, \%in);
}
I believe each in this case is an efficient way to pull a single key from the hash, but the documentation says each should not be used when deleting keys from the hash.
Otherwise I could use while (%in) { $_ = (keys(%in))[0] .... but that seems horribly inefficient for a very large hash.
Is there a better way to do this?
This seems to be a horrible thing to do, and it would be better if you explained what you were trying to achieve
However the problem with deleting hash elements while iterating using each is that each holds a state value for the hash that depends on the hash remaining unchanged
You can clear that state by calling keys (or values) on the same hash.
Here's an example which deletes the three elements with the given key and those before and after it in an attempt to emulate what your function_that_deletes_one_or_more_keys (which is what it should be called) might do
use strict;
use warnings 'all';
use feature 'say';
my %h = map +( $_ => 1), 0 .. 9;
while ( my $key = each %h ) {
say $key;
delete #h{$key-1, $key, $key+1};
keys %h; # Reset "each" state
}
I recommend that you don't use the global $_ variable for this
output
2
9
4
6
0
General advice without knowing details of the complexity to which you refer:
Change the function lines that delete keys to instead store the keys to be deleted in an array or hash (or file). After the first loop completes, loop through that array or hash (or file) and delete those keys from the first hash.
my #in_keys = keys %in;
for (#in_keys) {
if (exists $in{$_}) {
...
}
}
Or do as Borodin shows, resetting the iterator each time you know you've deleted at least one element.

Comparison to an array of a value [duplicate]

This question already has answers here:
How can I verify that a value is present in an array (list) in Perl?
(8 answers)
Closed 9 years ago.
I'm still feeling my way though perl and so there's probably a simple way of doing this but I can find it. I want to compare a single value say A or E to an array that may or may not contain that value, eg A B C D and then perform an action if they match. How should I set this up?
Thanks.
You filter each element of the array to see if it is the element you are looking for and then use the resulting array as a boolean value (not empty = true, empty = false):
#filtered_array = grep { $_ eq 'A' } #array;
if (#filtered_array) {
print "found it!\n";
}
If you store the list in an array then the only way is to examine each element individually in a loop, using grep, or for or any from List::MoreUtils. (grep is the worst of these, as it searches the entire array, even if a match has been found early on.) This is fine if the array is small, but you will hit performance probelms if the array has a significant size and you have to check it frequently.
You can speed things up by representing the same list in a hash, when a check for membership is just a single key lookup.
Alternatively, if the list is enormous, then it is best kept in a database, using SQLite.
Are you stuck on arrays?
Whenever in Perl you're talk about quickly looking up data, you should think in terms of hashes. A hash is a collection of data like an array, but it is keyed, and looking up the key is a very fast operation in Perl.
There's nothing that says the keys to your hash can't be your data, and it is very common in Perl to index an array with a hash in order to quickly search for values.
This turns your array #array into a hash called %arrays_hash.
use strict;
use warnings;
use feature qw(say);
use autodie;
my #array = qw(Alpha Beta Delta Gamma Ohm);
my %array_index;
for my $entry ( #array ) {
$array_index{$entry} = 1; # Doesn't matter. As long as it isn't blank or zero
}
Now, looking up whether or not your data is in your array is very quick. Just simply see if it's a key in your %array_index:
my $item = "Delta"; # Is this in my initial array?
if ( $array_index{$item} ) {
say "Yes! Item '$item' is in my array.";
}
else {
say "No. Item '$item' isn't in my array. David sad.";
}
This is so common, that you'll see a lot of programs that use the map command to index the array. Instead of that for loop, I could have done this:
my %array_index = ( map { $_ => 1 } #array );
or
my %array_index;
map { $array_index{$_} = 1 } #array;
You'll see both. The first one is a one liner. The map command takes each entry in the array, and puts it in $_. Then, it returns the results into an array. Thus, the map will return an array with your data in the even positions (0, 2, 4 8...) and a 1 in the odd positions (1, 3, 5...).
The second one is more literal and easier to understand (or about as easy to understand in a map command). Again, each item in your #array is being assigned to $_, and that is being used as the key in my %array_index hash.
Whether or not you want to use hashes depend upon the length of your array, and how many items of input you'll be searching for. If you're simply searching whether a single item is in your array, I'd probably use List::Utils or List::MoreUtils, or use a for loop to search each value of my array. If I am doing this for multiple values, I am better off with a hash.

Perl: Beginner. Which data structure should I use?

Okay, not sure where to ask this, but I'm a beginner programmer, using Perl. I need to create an array of an array, but I'm not sure if it would be better use array/hash references, or array of hashes or hash of arrays etc.
I need an array of matches: #totalmatches
Each match contains 6 elements(strings):
#matches = ($chapternumber, $sentencenumber, $sentence, $grammar_relation, $argument1, $argument2)
I need to push each of these elements into the #matches array/hash/reference, and then push that array/hash/reference into the #totalmatches array.
The matches are found based on searching a file and selecting the strings based on meeting the criteria.
QUESTIONS
Which data structure would you use?
Can you push an array into another array, as you would push an element into an array? Is this an efficient method?
Can you push all 6 elements simultaneously, or have to do 6 separate pushes?
When working with 2-D, to loop through would you use:
foreach (#totalmatches) {
foreach (#matches) {
...
}
}
Thanks for any advice.
Which data structure would you use?
An array for a ordered set of things. A hash for a set of named things.
Can you push an array into another array, as you would push an element into an array? Is this an efficient method?
If you try to push an array (1) into an array (2), you'll end up pushing all the elements of 1 into 2. That is why you would push an array ref in instead.
Can you push all 6 elements simultaneously, or have to do 6 separate pushes?
Look at perldoc -f push
push ARRAY,LIST
You can push a list of things in.
When working with 2-D, to loop through would you use:
Nested foreach is fine, but that syntax wouldn't work. You have to access the values you are dealing with.
for my $arrayref (#outer) {
for my $item (#$arrayref) {
$item ...
}
}
Do not push one array into another array.
Lists just join with each other into a new list.
Use list of references.
#create an anonymous hash ref for each match
$one_match_ref = {
chapternumber => $chapternumber_value,
sentencenumber => $sentencenumber_value,
sentence => $sentence_value,
grammar_relation => $grammar_relation_value,
arg1 => $argument1,
arg2 => $argument2
};
# add the reference of match into array.
push #all_matches, $one_match_ref;
# list of keys of interest
#keys = qw(chapternumber sentencenumber sentence grammer_relation arg1 arg2);
# walk through all the matches.
foreach $ref (#all_matches) {
foreach $key (#keys) {
$val = $$ref{$key};
}
# or pick up some specific keys
my $arg1 = $$ref{arg1};
}
Which data structure would you use?
An array... I can't really justify that choice, but I can't imagine what you would use as keys if you used a hash.
Can you push an array into another array, as you would push an element into an array? Is this an efficient method?
Here's the thing; in Perl, arrays can only contain scalar variables - the ones which start with $. Something like...
#matrix = ();
#row = ();
$arr[0] = #row; # FAIL!
... wont't work. You will have to instead use a reference to the array:
#matrix = ();
#row = ();
$arr[0] = \#row;
Or equally:
push(#matrix, \#row);
Can you push all 6 elements simultaneously, or have to do 6 separate pushes?
If you use references, you need only push once... and since you don't want to concatenate arrays (you need an array of arrays) you're stuck with no alternatives ;)
When working with 2-D, to loop through would you use:
I'd use something like:
for($i=0; $i<#matrix; $i++) {
#row = #{$matrix[$i]}; # de-reference
for($j=0; $j<#row; $j++) {
print "| "$row[$j];
}
print "|\n";
}
Which data structure would you use?
Some fundamental container properties:
An array is a container for ordered scalars.
A hash is a container for scalars obtained by a unique key (there can be no duplicate keys in the hash). The order of values added later is not available anymore.
I would use the same structure like ZhangChn proposed.
Use a hash for each match.
The details of the match then can be accessed by descriptive names instead of plain numerical indices. i.e. $ref->{'chapternumber'} instead of $matches[0].
Take references of these anonymous hashes (which are scalars) and push them into an array in order to preserve the order of the matches.
To dereference items from the data structure
get an item from the array which is a hash reference
retrieve any matching detail you need from the hash reference

In Perl, how do I process an entire hash?

I would like to process all elements of a hash table in Perl. How can I do that?
This is a question from the official perlfaq. We're importing the perlfaq to Stack Overflow.
(This is the official perlfaq answer, minus any subsequent edits)
There are a couple of ways that you can process an entire hash. You can get a list of keys, then go through each key, or grab a one key-value pair at a time.
To go through all of the keys, use the keys function. This extracts all of the keys of the hash and gives them back to you as a list. You can then get the value through the particular key you're processing:
foreach my $key ( keys %hash ) {
my $value = $hash{$key}
...
}
Once you have the list of keys, you can process that list before you process the hash elements. For instance, you can sort the keys so you can process them in lexical order:
foreach my $key ( sort keys %hash ) {
my $value = $hash{$key}
...
}
Or, you might want to only process some of the items. If you only want to deal with the keys that start with text:, you can select just those using grep:
foreach my $key ( grep /^text:/, keys %hash ) {
my $value = $hash{$key}
...
}
If the hash is very large, you might not want to create a long list of keys. To save some memory, you can grab one key-value pair at a time using each(), which returns a pair you haven't seen yet:
while( my( $key, $value ) = each( %hash ) ) {
...
}
The each operator returns the pairs in apparently random order, so if ordering matters to you, you'll have to stick with the keys method.
The each() operator can be a bit tricky though. You can't add or delete keys of the hash while you're using it without possibly skipping or re-processing some pairs after Perl internally rehashes all of the elements. Additionally, a hash has only one iterator, so if you use keys, values, or each on the same hash, you can reset the iterator and mess up your processing. See the each entry in perlfunc for more details.