Explanation on data structures - arrays

I once read the following Perl code involving iterations.
for my $j (0 .. $#{$dat[$Row]})
{
$vectors{ $dat[$Row][$j] } = $j;
}
What does
$vectors{ $dat[$Row][$j] }
stand for?
Is that equivalent to $vectors->$dat[$Row][$j] ?

what does $vectors{ $dat[$Row][$j] } stand for?
$dat[$Row] is a reference to an array. $dat[$Row][$j] is apparently an element in that array. Whatever value is contained in it, becomes a hash key in %vectors, which gets the value $j.
Is that equivalent $vectors->$dat[$Row][$j]
No, that would be referring to the variable $vectors, not %vectors.
A more readable way to write this might be:
my $aref = $dat[$Row];
for my $index (keys #$aref) {
my $key = $aref->[$index];
$vectors{$key} = $index;
}
Which also exemplifies the use of ->, to dereference a reference.

$vectors is a hash, $dat a multidimensional array (array of references) and $Row and $j two scalars. So you're setting the key given by $dat[$Row][$j] in the %vectors hash to $j.

$vectors{ $dat[$Row][$j] }
is short for
$vectors{ $dat[$Row]->[$j] }
If you spell it out,
# $Row is a row index.
# $j is a column index.
# (How inconsistent!)
my $row = $dat[$Row]; # A ref to an array.
my $key = $row->[$j]; # A value from the table.
$vectors{$key}

%vectors is a hash.
$vectors{$k} is the value in the hash for key $k
$dat[$Row][$j] is an element of a 2-D array (column $j, row $Row)
So the loop is creating a hash where the key is the contents and the value is the column index.

Related

perl: using push() on an array inside a hash

Is it possible to use Perl's push() function on an array inside a hash?
Below is what I believe to be the offending part of a larger program that I am working on.
my %domains = ();
open (TABLE, "placeholder.foo") || die "cannot read domtblout file\n";
while ($line = <TABLE>)
{
if (!($line =~ /^#/))
{
#split_line = split(/\t/, $line); # splits on tabs - some entries contain whitespace
if ($split_line[13] >= $domain_cutoff)
{
push($domains{$split_line[0]}[0], $split_line[19]); # adds "env from" coordinate to array
push($domains{$split_line[0]}[1], $split_line[20]); # adds "env to" coordinate to array
# %domains is a hash, but $domains{identifier}[0] and $domains{$identifier}[1] are both arrays
# this way, all domains from one sequence are stored with the same hash key, but can easily be processed iteratively
}
}
}
Later I try to interact with these arrays using
for ($i = 0, $i <= $domains{$identifier}[0], $i++)
{
$from = $domains{$identifier}[0][$i];
$to = $domains{$identifier}[1][$i];
$length = ($to - $from);
$tmp_seq =~ /.{$from}(.{$length})/;
print("$header"."$1");
}
but it appears as if the arrays I created are empty.
If $domains{$identifier}[0] is an array, then why can I not use the push statement to add an element to it?
$domains{identifier}[0] is not an array.
$domains{identifier}[0] is an array element, a scalar.
$domains{identifier}[0] is a reference to an array.
If it's
#array
when you have an array, it's
#{ ... }
when you have a reference to an array, so
push(#{ $domains{ $split_line[0] }[0] }, $split_line[19]);
References:
Mini-Tutorial: Dereferencing Syntax
References quick reference
perlref
perlreftut
perldsc
perllol

Create a multidimesional key of hash from array?

I want to create a multidimensional %hash from the #array.
Suppose #array is like
my #array=(1,2,3,4,5);
I want to assign #array last value as final value to multidimensional %hash i.e
%hash=(
1=>{
2=>
{
3=>
{
4=>5
}
}
}
)
Which means $hash{1}{2}{3}{4}=5;
I want to do it in something like:
for my $i (0..$#array){
#push $i as key until second last element and assign last element as value
}
Note : The #array may be of any size, Just I want to assign last element of #array as value to the keys of elements before the last element in %hash.
First, use pop to separate the value to assign from the keys. Then, you can use either of the following:
use Data::Diver qw( DiveVal );
my %hash;
DiveVal(\%hash, map \$_, #keys) = $val;
or
sub dive_val :lvalue {
my $p = \shift;
$p = \( $$p->{$_} ) for #_;
$$p
}
my %hash;
dive_val(\%hash, #keys) = $val;
dive_val works by having $p reference the next value to dereference and/or modify.
Pre-loop: $p references $hash (the anon scalar referencing %hash)
After loop pass 0: $p references $hash->{1}
After loop pass 1: $p references $hash->{1}{2}
After loop pass 2: $p references $hash->{1}{2}{3}
After loop pass 3: $p references $hash->{1}{2}{3}{4}
The extra level of indirection has many benefits.
It removes the need to treat the last key specially.
It removes the need to create the hash before it's dereferenced.
It removes the need for the root to be a reference to a hash. Instead, any scalar can be the root, even an undefined one.
It makes it easy to extend dive_val to support mixed array/hash structures.

Perl: Load file into hash

I'm struggling to understand logic behind hashes in Perl. Task is to load file in to hash and assign values to keys which are created using this file.
File contains alphabet with each letter on its own line:
a
b
c
d
e
and etc,.
When using array instead of hash, logic is simple: load file into array and then print each element with corresponding number using some counter ($counter++).
But now my question is, how can I read file into my hash, assign automatically generated values and sort it in that way where output is printed like this:
a:1
b:2
c:3
I've tried to first create array and then link it to hash using
%hash = #array
but it makes my hash non-sortable.
There are a number of ways to approach this. The most direct would be to load the data into the hash as you read through the file.
my %hash;
while(<>)
{
chomp;
$hash{$_} = $.; #Use the line number as your autogenerated counter.
}
You can also perform simliar logic if you already have a populated array.
for (0..$#array)
{
$hash{$array[$_]} = $_;
}
Although, if you are in that situation, map is the perlier way of doing things.
%hash = map { $array[$_] => $_ } #array;
Think of a hash as a set of pairs (key, value), where the keys must be unique. You want to read the file one line at a time, and add a pair to the hash:
$record = <$file_handle>;
$hash{$record} = $counter++;
Of course, you could read the entire file into an array at once and then assign to your hash. But the solution is not:
#records = <$file_handle>;
%hash = #records;
... as you found out. If you think in terms of (key, value) pairs, you will see that the above is equivalent to:
$hash{a} = 'b';
$hash{c} = 'd';
$hash{e} = 'f';
...
and so on. You still are going to need a loop, either an explicit one like this:
foreach my $rec (#records)
{
$hash{$rec} = $counter++;
}
or an implicit one like one of these:
%hash = map {$_ => $counter++} #records;
# or:
$hash{$_} = $counter++ for #records;
This code should generate the proper output, where my-text-file is the path to your data file:
my %hash;
my $counter = 0;
open(FILE, "my-text-file");
while (<FILE>) {
chomp;
$counter++;
$hash{$_} = $counter;
}
# Now to sort
foreach $key (sort(keys(%hash))) {
print $key . ":" . $hash{$key} . "\n";
}
I assume you want to sort the hash aplhabetically. keys(%hash) and values(%hash) return the keys and values of %hash as an array, respectively. Run the program on this file:
f
a
b
d
e
c
And we get:
a:2
b:3
c:6
d:4
e:5
f:1
I hope this helps you.

How to pass an array as value to a single key in a hash of perl?

my %hash;
my #chain;
foreach (my $i=0; $i<=7; $i++)
{
foreach (my $j=0; $j<=($#output); $j++)
{
if ($output[$j] =~ /chain1/)
{
push (#array, $output[$j]);
}
}
$hash{$chain[$i]} = [ #array ];
}
print "$hash{$chain[0]}\n";
The problem is I am not able to assign the arrays to unique keys in the hash. when I say print all the keys print the same output.
You keep adding to the same array.
for (...) {
{
my #array; <-- Add here
for (...) {
...
push #array, $output[$j];
...
}
$hash{$chain[$i]} = \#array; <-- No need to copy elements anymore.
}
Perl hash are designed to hold only scalar values. It can have a key and the value can be the address reference of the array (which is scalar). But if the array value need to be modified concatenate the contents of the array as a string with certain delimiter and store the string as key.
Hope this Helps.

How can I extract an array from a two-dimensional array in Perl?

I have once again forgotten how to get $_ to represent an array when it is in a loop of a two dimensional array.
foreach(#TWO_DIM_ARRAY){
my #ARRAY = $_;
}
That's the intention, but that doesn't work. What's the correct way to do this?
The line my #ARRAY = #$_; (instead of = $_;) is what you're looking for, but unless you explicitly want to make a copy of the referenced array, I would use #$_ directly.
Well, actually I wouldn't use $_ at all, especially since you're likely to want to iterate through #$_, and then you use implicit $_ in the inner loop too, and then you could have a mess figuring out which $_ is which, or if that's even legal. Which may have been why you were copying into #ARRAY in the first place.
Anyway, here's what I would do:
for my $array_ref (#TWO_DIM_ARRAY) {
# You can iterate through the array:
for my $element (#$array_ref) {
# do whatever to $element
}
# Or you can access the array directly using arrow notation:
$array_ref->[0] = 1;
}
for (#TWO_DIM_ARRAY) {
my #arr = #$_;
}
The $_ will be array references (not arrays), so you need to dereference it as:
my #ARRAY = #$_;

Resources