Use of uninitialized value in array value - arrays

I am having an array named #Lst_1. One of my elements is 0 in array.
Whenever I call that element. In example below the value stored in second index of an array is 0.
$Log_Sheet->write(Row,0,"$Lst_1[2]",$Format);
I am getting a warning saying
Use of uninitialized value within #Lst_1 in string.
Please help me do that.

First index of array is 0. Second element will be $List_1[1];
#!/usr/bin/env perl
use v5.22;
use warnings;
my #array = qw(foo bar);
# number of elements in array
say scalar(#array);
# last index of array
say $#array;
# undefined element (warn)
say $array[ $#array + 1];

If you just want to silence the error,
$Log_Sheet->write(Row, 0, $Lst_1[2] // 0, $Format);
This does use a feature of perl 5.10, but that's ancient enough you really should be using a sufficiently new perl to have it. I mean, there's a lot of ancient perl bugs, so it behooves you to be using a newer version.
As far as understanding the issue, no, $Lst_1[2] doesn't contain a 0. It contains an undef, which just happens to be treated mostly like 0 in numeric contexts.
Yes, I did remove the quotes around $Lst_1[2] - that was necessary, because "$Lst_1[2]" treats that undef like a string, so it's become the empty string for the purpose of a "$Lst_1[2]" // 0 test. (The empty string also happens to be treated like 0, so that doesn't change the behavior in a numeric context.)
It's not clear from your short excerpt whether #Lst_1 is less than 3 elements long, or if there's an explicit undef in #Lst_1. You'd need to show a larger excerpt of your code - or possibly even the whole thing and the data it is processing - for us to be able to determine that by looking. You could determine it, however, by adding something like the following in front of the line you gave:
if (#Lst_1 < 3) {
print "\#Lst_1 only has " . #Lst_1 . " elements\n"
} elsif (not defined($Lst_1[2])) {
print "\$Lst_1[2] is set to undef\n";
}
There are two basic ways a list can have an explicit undef element in it. The following code demonstrates both:
my #List = map "Index $_", 0 .. 3;
$List[2] = undef;
$List[5] = "Index 5";
use Data::Dump;
dd #List;
This will output
("Index 0", "Index 1", undef, "Index 3", undef, "Index 5")
The first undef was because I set it, the second was because there wasn't a fifth element but I put something in the sixth slot.

Related

how to replace values in an array of hashes properly in Perl?

As seen below, I have a foreach loop inside which, a value inside an array of hashes is being replaced with a value from another array of hashes.
The second foreach loop is just to print and test whether the values got assigned correctly.
foreach my $row (0 .. $#row_buff) {
$row_buff[$row]{'offset'} = $vars[$row]{'expression'};
print $row_buff[$row]{'offset'},"\n";
}
foreach (0 .. $#row_buff) {
print $row_buff[$_]{'offset'},"\n";
}
Here #row_buff and #vars are the two array of hashes. They are prefilled with values for all keys used.
The hashes were pushed into the arrays like so:
push #row_buff, \%hash;
ISSUE:
Let's say the print statement in the first foreach print's like this:
string_a
string_b
string_c
string_d
Then the print statement in the second foreach loop print's like so:
string_d
string_d
string_d
string_d
This is what confuses me. Both print statements are supposed to print the exact same way am I right? But the value that gets printed by the second print statement is just the last value alone in a repeated manner. Could someone please point me to what could be going wrong here? Any hint is greatly appreciated. This is my first time putting up a question so pardon me if I missed anything.
UPDATE
There was a bit of information that I could have added, sorry about that everyone. There was one more line before the foreach, it was like so:
#row_buff = (#row_buff) x $itercnt;
foreach my $row (0 .. $#row_buff) {
$row_buff[$row]{'offset'} = $vars[$row]{'expression'};
print $row_buff[$row]{'offset'},"\n";
}
foreach (0 .. $#row_buff) {
print $row_buff[$_]{'offset'},"\n";
}
$itercnt is an integer. I was using it to replicate the #row_buff that many number of times.
This clearly has to do with storing references on the array, instead of independent data. How that comes about isn't clear since details aren't given, but the following discussion should help.
Consider these two basic examples.
First, place a hash (reference) on an array, first changing a value each time
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd);
# use Storable qw(dclone);
my %h = ( a => 1, b => 2 );
my #ary_w_refs;
for my $i (1..3) {
$h{a} = $i;
push #ary_w_refs, \%h; # almost certainly WRONG
# push #ary_w_refs, { %h }; # *copy* data
# push #ary_w_refs, dclone \%h; # may be necessary, or just safer
}
dd $_ for #ary_w_refs;
I use Data::Dump for displaying complex data structures, for its simplicity and default compact output. There are other modules for this purpose, Data::Dumper being in the core (installed).
The above prints
{ a => 3, b => 2 }
{ a => 3, b => 2 }
{ a => 3, b => 2 }
See how that value for key a, that we changed in the hash each time, and so supposedly set for each array element, to a different value (1, 2, 3) -- is the same in the end, and equal to the one we assigned last? (This appears to be the case in the question.)
This is because we assigned a reference to the hash %h to each element, so even though every time through the loop we first change the value in the hash for that key in the end it's just the reference there, at each element, to that same hash.∗
So when the array is queried after the loop we can only get what is in the hash (at key a it's the last assigned number, 3). The array doesn't have its own data, only a pointer to hash's data.† (Thus hash's data can be changed by writing to the array as well, as seen in the example below.)
Most of the time, we want a separate, independent copy. Solution? Copy the data.
Naively, instead of
push #ary_w_refs, \%h;
we can do
push #ary_w_refs, { %h };
Here {} is a constructor for an anonymous hash,‡ so %h inside gets copied. So actual data gets into the array and all is well? In this case, yes, where hash values are plain strings/numbers.
But what when the hash values themselves are references? Then those references get copied, and #ary_w_refs again does not have its own data! We'll have the exact same problem. (Try the above with the hash being ( a => [1..10] ))
If we have a complex data structure, carrying references for values, we need a deep copy. One good way to do that is to use a library, and Storable with its dclone is very good
use Storable qw(dclone);
...
push #ary_w_refs, dclone \%h;
Now array elements have their own data, unrelated (but at the time of copy equal) to %h.
This is a good thing to do with a simple hash/array as well, to be safe from future changes, whereby the hash is changed but we forget about the places where it's copied (or the hash and its copies don't even know about each other).
Another example. Let's populate an array with a hashref, and then copy it to another array
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd pp);
my %h = ( a => 1, b => 2 );
my #ary_src = \%h;
say "Source array: ", pp \#ary_src;
my #ary_tgt = $ary_src[0];
say "Target array: ", pp \#ary_tgt;
$h{a} = 10;
say "Target array: ", pp(\#ary_tgt), " (after hash change)";
$ary_src[0]{b} = 20;
say "Target array: ", pp(\#ary_tgt), " (after hash change)";
$ary_tgt[0]{a} = 100;
dd \%h;
(For simplicity I use arrays with only one element.)
This prints
Source array: [{ a => 1, b => 2 }]
Target array: [{ a => 1, b => 2 }]
Target array: [{ a => 10, b => 2 }] (after hash change)
Target array: [{ a => 10, b => 20 }] (after hash change)
{ a => 100, b => 20 }
That "target" array, which supposedly was merely copied off of a source array, changes when the distant hash changes! And when its source array changes. Again, it is because a reference to the hash gets copied, first to one array and then to the other.
In order to get independent data copies, again, copy the data, each time. I'd again advise to be on the safe side and use Storable::dclone (or an equivalent library of course), even with simple hashes and arrays.
Finally, note a slightly sinister last case -- writing to that array changes the hash! This (second-copied) array may be far removed from the hash, in a function (in another module) that the hash doesn't even know of. This kind of an error can be a source of really hidden bugs.
Now if you clarify where references get copied, with a more complete (simple) representation of your problem, we can offer a more specific remedy.
∗ An important way of using a reference that is correct, and which is often used, is when the structure taken the reference of is declared as a lexical variable every time through
for my $elem (#data) {
my %h = ...
...
push #results, \%h; # all good
}
That lexical %h is introduced anew every time so the data for its reference on the array is retained, as the array persists beyond the loop, independently for each element.
It is also more efficient doing it this way since the data in %h isn't copied, like it is with { %h }, but is just "re-purposed," so to say, from the lexical %h that gets destroyed at the end of iteration to the reference in the array.
This of course may not always be suitable, if a structure to be copied naturally lives outside of the loop. Then use a deep copy of it.
The same kind of a mechanism works in a function call
sub some_func {
...
my %h = ...
...
return \%h; # good
}
my $hashref = some_func();
Again, the lexical %h goes out of scope as the function returns and it doesn't exist any more, but the data it carried and a reference to it is preserved, since it is returned and assigned so its refcount is non-zero. (At least returned to the caller, that is; it could've been passed yet elsewhere during the sub's execution so we may still have a mess with multiple actors working with the same reference.) So $hashref has a reference to data that had been created in the sub.
Recall that if a function was passed a reference, when it was called or during its execution (by calling yet other subs which return references), changed and returned it, then again we have data changed in some caller, potentially far removed from this part of program flow.
This is done often of course, with larger pools of data which can't just be copied around all the time, but then one need be careful and organize code (to be as modular as possible, for one) so to minimize chance of errors.
† This is a loose use of the word "pointer," for what a reference does, but if one were to refer to C I'd say that it's a bit of a "dressed" C-pointer
‡ In a different context it can be a block

Understanding referencing within perl arrays and how data is accessed

I could do with some help on perl and how it handles its arrays. (A long time ago) I used to do quite a lot of coding (hacking would be a better description, never pretty work) using php, java, js, etc but for various reasons I'm using perl for a project and i'm struggling to work out why I'm finding arrays such a pain.
For example, the following code:
#inflightsequences=([1,6,[["SRCIP","1.2.3.4"],["DSTIP","5.6.7.8"]]],[2,2,[["SRCIP","1.2.3.4"],["DSTIP","5.6.7.8"]]]);
foreach (#inflightsequences) {print Dumper #_};
where the definition of the array creates this (printed using Dumper)
$VAR1 = [
1,
6,
[
[
'SRCIP',
'1.2.3.4'
],
[
'DSTIP',
'5.6.7.8'
]
]
];
$VAR2 = [
2,
2,
[
[
'SRCIP',
'1.2.3.4'
],
[
'DSTIP',
'5.6.7.8'
]
]
];
(NB I'll refer to the data inside the array using VAR1 and VAR2 from now on so its clear which block I'm referring to, regardless of whether thats actually what Dumper calls it)
...but the foreach outputs absolutely nothing, when I expected it to cycle twice and output whats in VAR1 first then in VAR2. However
print Dumper #inflightsequences[0];
print Dumper #inflightsequences[1];
does print out VAR1 and VAR2 as expected.
Then I extract the first item from the #inflightsequences array
#dataset = shift(#inflightsequences);
and I expected print $dataset[1] to print out the first value (1) in what was VAR1 and print $dataset[2] to print the second value (6) but no, to achieve what I expected I have to do print $dataset[0][0] and print $dataset[0][1]. Why the extra [0]?
And thus I have got myself completely confused....
Thanks
--Chris
What is confusing you is that the elements of Perl arrays are always scalar values. You make arrays of arrays by using references for those scalars.
You can create an array reference either by building a named array and taking its reference
my #data = ( 'a', 'b', 'c' );
my $array_ref = \#data;
or by creating an anonymous array
my $array_ref = [ 'a', 'b', 'c' ];
The only difference between these two is that the data can be accessed through the name #data in the first case as well as through the reference $array_ref. To access elements of an array through a reference, you use the arrow operator, so
$array_ref->[0]
is the same as
$data[0]
The reason your foreach loop prints nothing is that you are dumping the contents of the array #_ which you have never mentioned before and is empty. #_ is the array that is set within a subroutine to the actual parameters passed when that subroutine is called. It has no use otherwise.
Remembering that array elements are scalars, and that if you don't specify a loop control variable then Perl will use $_, what you should have written is
foreach (#inflightsequences) { print Dumper($_) }
or, more Perlishly,
print Dumper($_) for #inflightsequences
The same applies to your statement
#dataset = shift(#inflightsequences)
which, again, because the contents of #inflightsequences are scalars is removing the first array reference and putting it into #dataset, which is now just a one-element array containing an array reference. That means you have moved $inflightsequences[0] to $dataset[0], which is now equal to
[1, 6, [ ["SRCIP", "1.2.3.4"], ["DSTIP", "5.6.7.8"] ] ]
not forgetting that the square brackets create a reference to an anonymous array. So, like our $array_ref->[0] above, you can access the first element of this array using $dataset[0]->[0]. And because Perl allows you to remove the arrow operator between pairs of square brackets (and curly brackets if we're using hashes) you can contract that to $dataset[0][0] which happens to be the value 1.
I hope that helps. You would do well to read perlref and experiment a little. Note that the Data::Dump module produces output much superior to Data::Dumper, but you may need to install it as it isn't a core module. Once it's installed the code will look like
use Data::Dump;
dd \#inflightsequences;
#inflightsequences is an array of array references so
$r=shift(#inflightsequences)
print $r->[0]
will show 1
and
print $r->[1]
will show 6
doing
#dataset=shift(#inflightsequences)
Makes an array from the result from the shift. So it's a array with one element, the shift result, which is accessed as $dataset[0]. $dataset[0]->[1] will give 6, for example

How to copy data to an array in perl?

I am trying access the data from the database and copy the data to an array. This is my code,
$sth = $dbh->prepare("SELECT * FROM abcd WHERE id=100 ");
$sth->execute;
$N=$sth->rows;
print "$N\n";
while (my #row_val = $sth->fetchrow_array()){
my ($uniqid, $time, $current, $id ) = #row_val;
$y[k]=$current;
$k++;
}
for ($k=0;$k<$N;$k++) {
print "$y[k]\t";
}
But it displays the same value for all $y[k]. How to copy the data from database to an array in perl?
You are using a bareword here:
$y[k]=$current;
# ^--- here, the "k" is a bareword
If you use warnings this will give a warning
Unquoted string "k" may clash with future reserved word at foo.pl line 10.
Argument "k" isn't numeric in array element at foo.pl line 10.
And the "k" will be interpreted as a string, will be converted to a number, which will be zero 0, so all your data is stored in $y[0].
This is why it is a very bad idea to not turn warnings on.
What you probably want instead is to push the new values onto the array:
push #y, $current;
This is, IMO, preferable to using an index, since it does all that work for you. Usually, you only want to specifically get involved with array indexes if the indexes themselves are of value for you, such as when comparing array elements.
This also means that your subsequent for loop
for ($k=0;$k<$N;$k++) {
print "$y[k]\t";
}
Is better written
for (#y) {
print "$_\t";
}
Although this is better written with join:
print join "\t", #y;
As a final note, you should always use
use strict;
use warnings;
It takes a small amount of learning to overcome the additional noise when using these pragmas, but it is well worth it in terms of learning and reducing your time spent debugging. I usually say that not using these pragmas is like covering up the low oil warning lamp in your car: Not knowing about the errors does not solve them.
This behaviour is because you are putting everything to index "k" - not any number just "k",
it is only a coincidence that its working at all :) - the "same value" is the last value - isnt it ? :)
SOLUTION:
1) variables are written with $ - keep that in mind when accessing $yourArray[$variableWithIndex]
2) $y[k]=$current; # wrong! you are trying to access "k" index
correct: $y[$k]=$current;
Didnt tested it - but this should work:
$sth = $dbh->prepare("SELECT * FROM abcd WHERE id=100 ");
$sth->execute;
$N=$sth->rows;
print "$N\n";
$k=0; # init first!
while (my #row_val = $sth->fetchrow_array()){
my ($uniqid, $time, $current, $id ) = #row_val;
$y[$k]=$current; # dont forget the $
$k++;
}
for ($k=0;$k<$N;$k++) {
print "$y[$k]\t"; # dont forget the $
}

How to get a single column of emails from a html textarea into array

I was thinking I could do this on my own but I need some help.
I need to paste a list of email addresses from a local bands mail list into a textarea and process them my Perl script.
The emails are all in a single column; delimited by newlines:
email1#email.com
email2#email.com
email3#email.com
email4#email.com
email5#email.com
I would like to obviously get rid of any whitespace:
$emailgoodintheory =~ s/\s//ig;
and I am running them through basic validation:
if (Email::Valid->address($emailgoodintheory)) { #yada
I have tried all kinds of ways to get the list into an array.
my $toarray = CGI::param('toarray');
my #toarraya = split /\r?\n/, $toarray;
foreach my $address(#toarraya) {
print qq~ $address[$arrcnt]<br /> ~:
$arrcnt++;
}
Above is just to test to see if I was successful. I have no need to print them.
It just loops through, grabs the schedules .txt file and sends each member the band schedule. All that other stuff works but I cannot get the textarea into an array!
So, as you can see, I am pretty lost.
Thank you sir(s), may I have another quick lesson?
You seem a bit new to Perl, so I will give you a thorough explanation why your code is bad and how you can improve it:
1 Naming conventions:
I see that this seems to be symbolic code, but $emailgoodintheory is far less readable than $emailGoodInTheory or $email_good_in_theory. Pick any scheme and stick to it, just don't write all lowercase.
I suppose that $emailgoodintheory holds a single email address. Then applying the regex s/\s//g or the transliteration tr/\s// will be enough; space characters are not case sensitive.
Using a module to validate adresses is a very good idea. :-)
2 Perl Data Types
Perl has three man types of variables:
Scalars can hold strings, numbers or references. They are denoted by the $ sigil.
Arrays can hold an ordered sequence of Scalars. They are denoted by the # sigil.
Hashes can hold an unordered set of Scalars. Some people tend to know them as dicitonaries. All keys and all values must be Scalars. Hashes are denoted by the % sigil.
A word on context: When getting a value/element from a hash/array, you have to change the sigil to the data type you want. Usually, we only recover one value (which always is a scalar), so you write $array[$i] or $hash{$key}. This does not follow any references so
my $arrayref = [1, 2, 3];
my #array = ($arrayref);
print #array[0];
will not print 123, but ARRAY(0xABCDEF) and give you a warning.
3 Loops in Perl:
Your loop syntax is very weird! You can use C-style loops:
for (my $i = 0; $i < #array; $i++)
where #array gives the length of the array, because we have a scalar context. You could also give $i the range of all possible indices in your array:
for my $i (0 .. $#array)
where .. is the range operator (in list context) and $#array gives the highest available index of our array. We can also use a foreach-loop:
foreach my $element (#array)
Note that in Perl, the keywords for and foreach are interchangeable.
4 What your loop does:
foreach my $address(#toarraya) {
print qq~ $address[$arrcnt]<br /> ~:
$arrcnt++;
}
Here you put each element of #toarraya into the scalar $address. Then you try to use it as an array (wrong!) and get the index $arrcnt out of it. This does not work; I hope your program died.
You can use every loop type given above (you don't need to count manually), but the standard foreach loop will suit you best:
foreach my $address (#toarraya){
print "$address<br/>\n";
}
A note on quoting syntax: while qq~ quoted ~ is absolutely legal, this is the most obfuscated code I have seen today. The standard quote " would suffice, and when using qq, try to use some sort of parenthesis (({[<|) as delimiter.
5 complete code:
I assume you wanted to write this:
my #addressList = split /\r?\n/, CGI::param('toarray');
foreach my $address (#addressList) {
# eliminate white spaces
$address =~ s/\s//g;
# Test for validity
unless (Email::Valid->address($address)) {
# complain, die, you decide
# I recommend:
print "<strong>Invalid address »$address«</strong><br/>";
next;
}
print "$address<br/>\n";
# send that email
}
And never forget to use strict; use warnings; and possibly use utf8.

In Perl, how do I create a hash whose keys come from a given array?

Let's say I have an array, and I know I'm going to be doing a lot of "Does the array contain X?" checks. The efficient way to do this is to turn that array into a hash, where the keys are the array's elements, and then you can just say if($hash{X}) { ... }
Is there an easy way to do this array-to-hash conversion? Ideally, it should be versatile enough to take an anonymous array and return an anonymous hash.
%hash = map { $_ => 1 } #array;
It's not as short as the "#hash{#array} = ..." solutions, but those ones require the hash and array to already be defined somewhere else, whereas this one can take an anonymous array and return an anonymous hash.
What this does is take each element in the array and pair it up with a "1". When this list of (key, 1, key, 1, key 1) pairs get assigned to a hash, the odd-numbered ones become the hash's keys, and the even-numbered ones become the respective values.
#hash{#array} = (1) x #array;
It's a hash slice, a list of values from the hash, so it gets the list-y # in front.
From the docs:
If you're confused about why you use
an '#' there on a hash slice instead
of a '%', think of it like this. The
type of bracket (square or curly)
governs whether it's an array or a
hash being looked at. On the other
hand, the leading symbol ('$' or '#')
on the array or hash indicates whether
you are getting back a singular value
(a scalar) or a plural one (a list).
#hash{#keys} = undef;
The syntax here where you are referring to the hash with an # is a hash slice. We're basically saying $hash{$keys[0]} AND $hash{$keys[1]} AND $hash{$keys[2]} ... is a list on the left hand side of the =, an lvalue, and we're assigning to that list, which actually goes into the hash and sets the values for all the named keys. In this case, I only specified one value, so that value goes into $hash{$keys[0]}, and the other hash entries all auto-vivify (come to life) with undefined values. [My original suggestion here was set the expression = 1, which would've set that one key to 1 and the others to undef. I changed it for consistency, but as we'll see below, the exact values do not matter.]
When you realize that the lvalue, the expression on the left hand side of the =, is a list built out of the hash, then it'll start to make some sense why we're using that #. [Except I think this will change in Perl 6.]
The idea here is that you are using the hash as a set. What matters is not the value I am assigning; it's just the existence of the keys. So what you want to do is not something like:
if ($hash{$key} == 1) # then key is in the hash
instead:
if (exists $hash{$key}) # then key is in the set
It's actually more efficient to just run an exists check than to bother with the value in the hash, although to me the important thing here is just the concept that you are representing a set just with the keys of the hash. Also, somebody pointed out that by using undef as the value here, we will consume less storage space than we would assigning a value. (And also generate less confusion, as the value does not matter, and my solution would assign a value only to the first element in the hash and leave the others undef, and some other solutions are turning cartwheels to build an array of values to go into the hash; completely wasted effort).
Note that if typing if ( exists $hash{ key } ) isn’t too much work for you (which I prefer to use since the matter of interest is really the presence of a key rather than the truthiness of its value), then you can use the short and sweet
#hash{#key} = ();
I always thought that
foreach my $item (#array) { $hash{$item} = 1 }
was at least nice and readable / maintainable.
There is a presupposition here, that the most efficient way to do a lot of "Does the array contain X?" checks is to convert the array to a hash. Efficiency depends on the scarce resource, often time but sometimes space and sometimes programmer effort. You are at least doubling the memory consumed by keeping a list and a hash of the list around simultaneously. Plus you're writing more original code that you'll need to test, document, etc.
As an alternative, look at the List::MoreUtils module, specifically the functions any(), none(), true() and false(). They all take a block as the conditional and a list as the argument, similar to map() and grep():
print "At least one value undefined" if any { !defined($_) } #list;
I ran a quick test, loading in half of /usr/share/dict/words to an array (25000 words), then looking for eleven words selected from across the whole dictionary (every 5000th word) in the array, using both the array-to-hash method and the any() function from List::MoreUtils.
On Perl 5.8.8 built from source, the array-to-hash method runs almost 1100x faster than the any() method (1300x faster under Ubuntu 6.06's packaged Perl 5.8.7.)
That's not the full story however - the array-to-hash conversion takes about 0.04 seconds which in this case kills the time efficiency of array-to-hash method to 1.5x-2x faster than the any() method. Still good, but not nearly as stellar.
My gut feeling is that the array-to-hash method is going to beat any() in most cases, but I'd feel a whole lot better if I had some more solid metrics (lots of test cases, decent statistical analyses, maybe some big-O algorithmic analysis of each method, etc.) Depending on your needs, List::MoreUtils may be a better solution; it's certainly more flexible and requires less coding. Remember, premature optimization is a sin... :)
In perl 5.10, there's the close-to-magic ~~ operator:
sub invite_in {
my $vampires = [ qw(Angel Darla Spike Drusilla) ];
return ($_[0] ~~ $vampires) ? 0 : 1 ;
}
See here: http://dev.perl.org/perl5/news/2007/perl-5.10.0.html
Also worth noting for completeness, my usual method for doing this with 2 same-length arrays #keys and #vals which you would prefer were a hash...
my %hash = map { $keys[$_] => $vals[$_] } (0..#keys-1);
Raldi's solution can be tightened up to this (the '=>' from the original is not necessary):
my %hash = map { $_,1 } #array;
This technique can also be used for turning text lists into hashes:
my %hash = map { $_,1 } split(",",$line)
Additionally if you have a line of values like this: "foo=1,bar=2,baz=3" you can do this:
my %hash = map { split("=",$_) } split(",",$line);
[EDIT to include]
Another solution offered (which takes two lines) is:
my %hash;
#The values in %hash can only be accessed by doing exists($hash{$key})
#The assignment only works with '= undef;' and will not work properly with '= 1;'
#if you do '= 1;' only the hash key of $array[0] will be set to 1;
#hash{#array} = undef;
You could also use Perl6::Junction.
use Perl6::Junction qw'any';
my #arr = ( 1, 2, 3 );
if( any(#arr) == 1 ){ ... }
If you do a lot of set theoretic operations - you can also use Set::Scalar or similar module. Then $s = Set::Scalar->new( #array ) will build the Set for you - and you can query it with: $s->contains($m).
You can place the code into a subroutine, if you don't want pollute your namespace.
my $hash_ref =
sub{
my %hash;
#hash{ #{[ qw'one two three' ]} } = undef;
return \%hash;
}->();
Or even better:
sub keylist(#){
my %hash;
#hash{#_} = undef;
return \%hash;
}
my $hash_ref = keylist qw'one two three';
# or
my #key_list = qw'one two three';
my $hash_ref = keylist #key_list;
If you really wanted to pass an array reference:
sub keylist(\#){
my %hash;
#hash{ #{$_[0]} } = undef if #_;
return \%hash;
}
my #key_list = qw'one two three';
my $hash_ref = keylist #key_list;
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my #a = qw(5 8 2 5 4 8 9);
my #b = qw(7 6 5 4 3 2 1);
my $h = {};
#{$h}{#a} = #b;
print Dumper($h);
gives (note repeated keys get the value at the greatest position in the array - ie 8->2 and not 6)
$VAR1 = {
'8' => '2',
'4' => '3',
'9' => '1',
'2' => '5',
'5' => '4'
};
You might also want to check out Tie::IxHash, which implements ordered associative arrays. That would allow you to do both types of lookups (hash and index) on one copy of your data.

Resources