How to create two dimensional array with 2 arrays - arrays

How to create multiple dimentional array with 2 arrays?
#param=("param1","param2","param3");
#value=("value1_1, value1_2, value1_3", "value2_1, value2_2, value2_3","value3_1, value3_2, value3_3");
Output:
#out=(["param1"]["value1_1", "value1_2", "value1_3"], ["param2"]["value2_1", "value2_2", "value2_3"], ["param3"]["value3_1", "value3_2", "value3_3"])
I've tried this way:
$j=0;
foreach $i(#param1){
push #{$out[$i]}, split(", ", $value[$j]);
$j++;}

It's not exactly clear to me what data structure you want to create.
However, I assume you are trying to create a hash of arrays (a hash table is also known as dictionary or associative array), not an array.
The difference in Perl is that an array always uses integers as keys, whereas a hash always uses strings.
The resulting data structure would then look like
%out = (
'param1' => ['value1_1', 'value1_2', 'value1_3'],
'param2' => ['value2_1', 'value2_2', 'value2_3'],
'param3' => ['value3_1', 'value3_2', 'value3_3'],
);
We can create this data structure like so:
my %out;
for my $i (0 .. $#param) {
$out{$param[$i]} = [split /,\s*/, $value[$i]];
}
Note that $#foo is the highest index in the #foo array. Therefore, 0 .. $#foo would be the range of all indices in #foo. Also note that entries in hashes are accessed with a curly brace subscript $hash{$key}, unlike arrays which use square brackets $array[$index].
You can access multiple elements in a hash or array at the same time by using a slice – #foo{'a', 'b', 'c'} is equivalent to ($foo{a}, $foo{b}, $foo{c}). We can also transform a list of elements by using the map {BLOCK} LIST function. Together, this allows for the following solution:
my %out;
#out{#param} = map { [split /,\s*/, $_] } #value;
Inside the map block, the $_ variable is set to each item in the input list in turn.
To learn more about complex data structures, read (in this order):
perldoc perreftut
perldoc perllol
perldoc perldsc
You can also read the documentation for the map function and for the foreach loop.

Related

2D Array Printing as Reference

I have the code similar to below:
my #array1 = (); #2d array to be used
my $string1 = "blank1";
my $string2 = "blank2";
my $string3 = "blank3";
my #temp = ($string1, $string2, $string3);
push (#array1, \#temp);
The reason I am assigning the strings and then putting them into an array is because they are in a loop and the values get updated in the loop (#array1 is not declared in the loop).
When I run my program, it only gives me a reference to an array rather than an actual 2D array. How can I get it to print out the content as a 2D array and not as a reference or flattened out to a 1D array?
I would like an output like [[blank1, blank2, blank3],....] so i can access it like $array1[i][j]
An array can only have scalars for elements. Thus this includes references, to arrays for example, what enables us to build complex data structures. See perldsc, Tom's Perl Data Structure Cookbook.
Elements of those ("second-level") arrays are accessed by dereferencing, so $array1[0]->[1] is the second element of the array whose reference is the first element of the top-level array (#array1). Or, for convenience, a simpler syntax is allowed as well: $array1[0][1].
If we want a list of all elements of a second-level array then dereference it with #, like:
my #l2 = #{ $array1[0] }; # or, using
my #l2 = $array1[0]->#*; # postfix dereferencing
Or, to get just a few elements of the array, but in one scoop -- a slice
my #l2_slice = #{$array1[0]}[1..2]; # or
my #l2_slice = $array1[0]->#[1..2]; # postfix reference slice
what returns the list with the second and third elements of the same second-level array.
The second lines are of a newer syntax called postfix dereferencing, stable as of v5.24. It avails us with the same logic for getting elements as when we drill for a single one, by working left-to-right all the way. So ->#* to get a list of all elements for an arrayref,->%* for a hashref (etc). See for instance a perl.com article and an Effective Perler article.
There is a thing to warn about when it comes to multidimensional structures built with references. There are two distinct ways to create them: by using references to existing, named, variables
my #a1 = 5 .. 7;
my %h1 = ( a => 1, b => 2 );
my #tla1 = (\#a1, \%h1);
or by using anonymous ones, where arrayrefs are constructed by [] and hashrefs by {}
my #tla2 = ( [ 5..7 ], { a => 1, b => 2 } );
The difference is important to keep in mind. In the first case, the references to variables that the array carries can be used to change those variables -- if we change elements of #tla1 then we really change the referred variables
$tla1[0][1] = 100; # now #a1 == 5, 100, 7
Also, changing variables with references in #tla1 is seen via the top-level array as well.
With anonymous arrays and hashes in #tla this isn't the case as elements (references) of #tla access independent data, which cannot be accessed (and changed) in any other way.
Both of these ways to build complex data structures have their uses.

How do I pass in the name of an array index in perl?

Background
I have these four lines of code that I am trying to extract into one function:
my #firstList = split /\s+/, $doubleArray{A_I}{B_I};
#firstList = shuffle(#firstList);
my #secondList = split /\s+/, $doubleArray{A_I}{C_I};
#secondList = shuffle(#secondList);
Since the only functional difference is the second index of the two dimensional array , I wanted to make it so that the second index ("B_I" and "C_I") are passed into a function.
Proposed solution
In other words I want to make a function like the one below:
my funkyFunc($){
my $index = shift;
my #list = split /\s+/, $doubleArray{A_I}{$index};
#list = shuffle(#list);
return #list;
}
I would intend to use it as such:
my #firstList = funkyFunc("B_I");
my #secondList = funkyFunc("C_I");
However, I'm unsure as to how perl would interpret "B_I" or "C_I", or likewise B_I and C_I. Because of this complexity, I wanted to know...
Question
Is there a way in perl to pass in the name of an array index?
Those aren't arrays, those are hashes (dictionaries, associative arrays in other languages). With that, the B_I and C_I are then treated as bareword strings. If these were actual arrays, then these would be treated as bareword function calls, and you'd have to have those available to be called in the caller, and no need for quoting there.
Since you're using hashes, the keys are strings, and you passing in 'B_I' will result in $index being a string of B_I, and since your {$index} has something that isn't allowed in a bareword string (the $), perl will interpret it as a variable instead of a literal, and everything will work exactly the way you want.

Not able to divide each row of a csv file in the form of array using perl

I am stucked in a problem wherein I am parsing a csv file. The CSV file looks like-
CPU Name,DISABLE,Memory,Encoding,Extra Encoding
,b,d,,
String1,YES,1TB,Enabled,Enabled
String2,NO,1TB,Enabled,Enabled
String3,YES,1TB,Enabled,Enabled
I want to capture the first two rows in two different arrays. The code that I am using to do it is-
my $row_no =0;
while(my $row=<$fi>){
chomp($row);
$row=~ s/\A\s+//g;
$row=~s/\R//g;
#say $row;
if($row_no==0)
{
#say $row;
my #name_initial = split(',',$row);
say length(#name_initial);
say #name_initial;
}
elsif($row_no==1)
{
#say $row;
#data_type_initial =split(',',$row);
say length(#data_type_initial);
say #data_type_initial;
}
$row_no++;
}
Now I formed two arrays from topmost two lines in file (#name_initial and #data_type_initial respectively).When I am printing these array I can see all the 5 values but when I am printing the length of array it is showing length of each array as 1. When I am printing the element using index of arrays I find each element in place then why it is showing length as 1. Also second array which is formed from second line of csv file is printed as "bd". All the null values are gone and although it is containing two values 'b' and 'd'. Its length is printed as 1.
I want to convert the row of csv file in array with all the null and non_NULL values so that I can iterate on the array elements and can give conditions based on null and non null values.How can I do that???
Have a look at perldoc length. It says this:
length EXPR
length
Returns the length in characters of the value of
EXPR. If EXPR is omitted, returns the length of $_. If EXPR is
undefined, returns undef.
This function cannot be used on an entire array or hash to find out
how many elements these have. For that, use scalar #array and scalar
keys %hash, respectively.
Like all Perl character operations, length normally deals in logical
characters, not physical bytes. For how many bytes a string encoded as
UTF-8 would take up, use length(Encode::encode('UTF-8', EXPR)) (you'll
have to use Encode first). See Encode and perlunicode.
In particular, the bit that says "This function cannot be used on an entire array or hash to find out how many elements these have. For that, use scalar #array and scalar keys %hash, respectively".
So you're using the wrong approach here. Instead of say length(#array), you need say scalar(#array).
To explain the results you're getting. length() expects to be given a scalar value (a string) to measure. So it treats your array as a scalar (effectively adding an invisible call to scalar()) and gets back the number of elements in the array (which is "5") and length() then tells you the number of elements in that string - which is 1.
It's also worth pointing out that you don't need to keep track of your own $row_no variable. Perl has a built-in variable called $. which contains the current record number.
Using that knowledge (and adding little whitespace) gives us something like this:
while (my $row = <$fi>) {
chomp($row);
$row =~ s/\A\s+//g;
$row =~s/\R//g;
#say $row;
if ($. == 0) {
#say $row;
my #name_initial = split(/,/, $row);
say scalar(#name_initial);
say #name_initial;
} elsif ($. == 1) {
#say $row;
#data_type_initial = split(/,/, $row);
say scalar(#data_type_initial);
say #data_type_initial;
}
}
Update: You sneaked a couple of extra questions in at the end of this one. I'd suggest that you raise those separately.

calculate the index of a array element reference

Is it possible to calculate the index of a referenced scalar from an array?
in C you can use pointer arithmetic to retrieve the index.
SomeType array[500];
const SomeType* e = &array[42];
// [...]
size_t index = e-array;
Is there some similar way in Perl?
my #array = (1,2,3,4,5,6,7,8,9,0);
my $e = \$array[4];
# [...]
my $index = '???';
The reason:
I have a relatively large (> 6Mio entries) Array with equally structured geometrically related data.
I also have some kind of priority based queue that contains references to this array. while processing this queue new elements are added and the queue has to be resorted. Since this queue also will grow quite large. and the priorities of the elements changes and are derived from the array element and its neighbors, i would like to avoid complex entries in the queue (memory size and allocation performance) and have only the reference their to directly access the information from the array.
But it seems that using indexes in the task-list would be the best option.
List::MoreUtils provides routines such as:
first_index
last_index
bsearch_index
indexes
etc. Depending on the circumstances, using one of these may be more efficient than just using plain old grep:
my #i = grep $array[$_] == $v, 0 .. $#array;
$e contains a reference, and you can compare references for equality.
my #array = (0,0,0,0,0,0,0,0,0,0,0);
my $e = \$array[4];
#..
my $index =$#array;
$index-- while ($e ne \$array[$index] && $index >=0);
print $index;
prints out 4.

Can BerkeleyDB in perl handle a hash of hashes of hashes (up to n)?

I have a script that utilizes a hash, which contains four strings as keys whose values are hashes. These hashes also contain four strings as keys which also have hashes as their values. This pattern continues up to n-1 levels, which is determined at run-time. The nth-level of hashes contain integer (as opposed to the usual hash-reference) values.
I installed the BerkeleyDB module for Perl so I can use disk space instead of RAM to store this hash. I assumed that I could simply tie the hash to a database, and it would work, so I added the following to my code:
my %tags = () ;
my $file = "db_tags.db" ;
unlink $file;
tie %tags, "BerkeleyDB::Hash",
-Filename => $file,
-Flags => DB_CREATE
or die "Cannot open $file\n" ;
However, I get the error:
Can't use string ("HASH(0x1a69ad8)") as a HASH ref while "strict refs" in use at getUniqSubTreeBDB.pl line 31, line 1.
To test, I created a new script, with the code (above) that tied to hash to a file. Then I added the following:
my $href = \%tags;
$tags{'C'} = {} ;
And it ran fine. Then I added:
$tags{'C'}->{'G'} = {} ;
And it would give pretty much the same error. I am thinking that BerkeleyDB cannot handle the type of data structure I am creating. Maybe it was able to handle the first level (C->{}) in my test because it was just a regular key -> scaler?
Anyways, any suggestions or affirmations of my hypothesis would be appreciated.
Use DBM::Deep.
my $db = DBM::Deep->new( "foo.db" );
$db->{mykey} = "myvalue";
$db->{myhash} = {};
$db->{myhash}->{subkey} = "subvalue";
print $db->{myhash}->{subkey} . "\n";
The code I provided yesterday would work fine with this.
sub get_node {
my $p = \shift;
$p = \( ($$p)->{$_} ) for #_;
return $p;
}
my #seqs = qw( CG CA TT CG );
my $tree = DBM::Deep->new("foo.db");
++${ get_node($tree, split //) } for #seqs;
No. BerkeleyDB stores pairs of one key and one value, where both are arbitrary bytestrings. If you store a hashref as the value, it'll store the string representation of a hashref, which isn't very useful when you read it back (as you noticed).
The MLDBM module can do something like you describe, but it works by serializing the top-level hashref to a string and storing that in the DBM file. This means it has to read/write the entire top-level hashref every time you access or change a value in it.
Depending on your application, you may be able to combine your keys into a single string, and use that as the key for your DBM file. The main limitation with that is that it's difficult to iterate over the keys of one of your interior hashes.
You might use the semi-obsolete multidimensional array emulation for this. $foo{$a,$b,$c} is interpreted as $foo{join($;, $a, $b, $c)}, and that works with tied hashes also.
No; it can only store strings. But you can use the →filter_fetch_value and →filter_store_value to define "filters" that will automatically freeze arbitrary structures to strings before storing, and to convert back when fetching. There are analogous hooks for marshalling and unmarshalling non-string keys.
Beware though: using this method to store objects that share subobjects will not preserve the sharing. For example:
$a = [1, 2, 3];
$g = { array => $a };
$h = { array => $a };
$db{g} = $g;
$db{h} = $h;
#$a = ();
push #{$db{g}{array}}, 4;
print #{$db{g}{array}}; # prints 1234, not 4
print #{$db{h}{array}}; # prints 123, not 1234 or 4
%db here is a tied hash; if it were an ordinary hash the two prints would both print 4.
While you can't store normal multidimensional hashes in a BerkeleyDB tied hash, you can use emulated multidimensional hashes with a syntax like $tags{ 'C', 'G'}. This creates a single key that looks like ('C' . $; . 'G')
I had the same question, found this. Might be useful for you as well.
Storing data structures as values in BDB
Often, we might be interested in storing complex data structures: arrays, hashtables,… whose elements can be simple values, of references to other data structures. To do this, we need to serialize the data structure: convert it to a string that can be stored in the database, and can be later converted back into the original data structure using a deserialization procedure.
There are several perl modules available to perform this serialization/deserialization process. One of the most popular is JSON::XS. The next example shows how to use this module:
use JSON::XS;
# Data to be stored
my %structure;
# Convert the data into a json string
my $json = encode_json(%structure);
# Save it in the database
$dbh->db_put($key,$json);
To retrieve the original structure, we perform the inverse operation:
# Retrieve the json string from the database
$dbh->db_get($key, $json);
# Deserialize the json string into a data structure
my $hr_structure = decode_json($json);
In perl you can do this. You are using references beyond the first level.
use GDBM_File;
use Storable;
use MLDBM qw(GDBM_File Storable);
my %hash;
my %level_2_hash;
my %level_3_hash1 = (key1 => x, key2 => y, key3 => z)
my %level_3_hash2 = (key10 => a, key20 => b, key30 => c)
$level_2_hash = (keyA => /%level_3_hash1, keyB => level_3_hash2)
$hash{key} = \%level_2_hash;
This can be found in the online beginning perl book in chapter 13.

Resources