i don't understand #unordered{#array} in perl - perl

I'm inexperienced programmer in Perl. I'm already reading the book Beginning Perl Curtis “Ovid” Poe and have problem with this code.
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
my #array = ( 3, 4, 1, 4, 7, 7, 4, 1, 3, 8 );
my %unordered;
#unordered{#array} = undef;
foreach my $key (keys %unordered) {
print “Unordered: $key\n”;
}
#unordered{#array} = undef;
what does this code mean ?
Could anyone explain me ?

#unordered{#array} = undef
This is called a hash slice . You can read more about it here or here or here. In other words, it acts on multiple keys of the hash(the keys are described by #array, and in this particular case, it assigns the value undef to each of those keys).

I assume it is to show you some things about the keys of hashes.
You have the #array and then create entries in your hash %unordered with the values from your array as keys and no values. This happens in this line:
#unordered{#array} = undef;
You then iterate through all the keys of that hash and print them. So you can see what keys there are and in what order they are in the hash.
You will probably notice how every key exists only once despite several values being more than once in the array. This is because keys of hashes always are unique.
Also you could see how the keys are in no particular order as the keys of a hash may be ordered in any way the implementation likes.

Yes, I understand all what you're explaining to me. Tkank you for your time.
But why, first of all we initiation an array with numbers, then create a hash (blank hash), then we create entries in hash %unordered with the values from array as keys and no values.
By in loop for, we use an "%" (%unordered). We change array "#array" to hash "%unordered", and then we use in loop for again %unordered.
It looks a bit strange

Related

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.

How to get all different values(or keys) in a hash in Perl

I have a little problem with some hashes.
if i have a hash containing, John,John,John,Bob,Bob,Paul - is there then a function that can return just:
John, Bob, Paul.
In other words i want to get all different values(or keys if value is not possible) - but only once :).
I hope u understand my question, thx :)
TIMTOWTDI:
my #unique = keys { reverse %hash };
Note the performance caveat with reverse though:
This operator is also handy for inverting a hash, although there are
some caveats. If a value is duplicated in the original hash, only one
of those can be represented as a key in the inverted hash. Also, this
has to unwind one hash and build a whole new one, which may take some
time on a large hash, such as from a DBM file.
%by_name = reverse %by_address; # Invert the hash
Something like this may help you:
use List::MoreUtils qw{ uniq };
my %hash = ( a => 'Paul', b => 'Paul', c => 'Peter' );
my #uniq_names = uniq values %hash;
print "#uniq_names\n";
Keys are uniq always.
De-duping is easily (and idiomatically) done by using a hash:
my #uniq = keys { map { $_ => 1 } values %hash };
A simple enough approach that does not require installing modules. Since hash keys must be unique, any list of strings become automatically de-duped when used as keys in the same hash.
Note the use of curly braces forming an anonymous hash { ... } around the map statement. That is required for keys.
Note also that values %hash can be any list of strings, such as one or more arrays, subroutine calls and whatnot.

Perl: simple foreach on hash hands mixed results? [duplicate]

activePerl 5.8 based
#!C:\Perl\bin\perl.exe
use strict;
use warnings;
# declare a new hash
my %some_hash;
%some_hash = ("foo", 35, "bar", 12.4, 2.5, "hello",
"wilma", 1.72e30, "betty", "bye\n");
my #any_array;
#any_array = %some_hash;
print %some_hash;
print "\n";
print #any_array;
print "\n";
print $any_array[0];
print "\n";
print $any_array[1];
print "\n";
print $any_array[2];
print "\n";
print $any_array[3];
print "\n";
print $any_array[4];
print "\n";
print $any_array[5];
print "\n";
print $any_array[6];
print "\n";
print $any_array[7];
print "\n";
print $any_array[8];
print "\n";
print $any_array[9];
Output as this
D:\learning\perl>test.pl
bettybye
bar12.4wilma1.72e+030foo352.5hello
bettybye
bar12.4wilma1.72e+030foo352.5hello
betty
bye
bar
12.4
wilma
1.72e+030
foo
35
2.5
hello
D:\learning\perl>
What decided the elements print order in my sample code?
Any rule to follow when print a mixed(strings, numbers) hash in Perl? Thank you.
bar12.4wilma1.72e+030foo352.5hello
[Updated]
With you guys help, i updated the code as below.
#!C:\Perl\bin\perl.exe
use strict;
use warnings;
# declare a new hash
my %some_hash;
%some_hash = ("foo", 35, "bar", 12.4, 2.5, "hello",
"wilma", 1.72e30, "betty", "bye");
my #any_array;
#any_array = %some_hash;
print %some_hash;
print "\n";
print "\n";
print #any_array;
print "\n";
print "\n";
my #keys;
#keys = keys %some_hash;
for my $k (sort #keys)
{
print $k, $some_hash{$k};
}
output
D:\learning\perl>test.pl
bettybyebar12.4wilma1.72e+030foo352.5hello
bettybyebar12.4wilma1.72e+030foo352.5hello
2.5hellobar12.4bettybyefoo35wilma1.72e+030
D:\learning\perl>
Finially, after called keys and sort functions. The hash keys print followed the rule below
2.5hellobar12.4bettybyefoo35wilma1.72e+030
Elements of a hash are printed out in their internal order, which can not be relied upon and will change as elements are added and removed. If you need all of the elements of a hash in some sort of order, sort the keys, and use that list to index the hash.
If you are looking for a structure that holds its elements in order, either use an array, or use one of the ordered hash's on CPAN.
the only ordering you can rely upon from a list context hash expansion is that key => value pairs will be together.
From perldoc -f keys:
The keys of a hash are returned in an apparently random order. The actual random order is subject to change in future versions of Perl, but it is guaranteed to be the same order as either the values or each function produces (given that the hash has not been modified). Since Perl 5.8.1 the ordering is different even between different runs of Perl for security reasons (see Algorithmic Complexity Attacks in perlsec).
...
Perl has never guaranteed any ordering of the hash keys, and the ordering has already changed several times during the lifetime of Perl 5. Also, the ordering of hash keys has always been, and continues to be, affected by the insertion order.
Also note that while the order of the hash elements might be randomised, this "pseudoordering" should not be used for applications like shuffling a list randomly (use List::Util::shuffle() for that, see List::Util, a standard core module since Perl 5.8.0; or the CPAN module Algorithm::Numerical::Shuffle), or for generating permutations (use e.g. the CPAN modules Algorithm::Permute or Algorithm::FastPermute), or for any cryptographic applications.
Note: since you are evaluating a hash in list context, you are at least guaranteed that each key is followed by its corresponding value; e.g. you will never see an output of a 4 b 3 c 2 d 1.
I went over your code and made some notes that I think you will find helpful.
use strict;
use warnings;
# declare a new hash and initialize it at the same time
my %some_hash = (
foo => 35, # use the fat-comma or '=>' operator, it quotes the left side
bar => 12.4,
2.5 => "hello",
wilma => 1.72e30,
betty => "bye", # perl ignores trailing commas,
# the final comma makes adding items to the end of the list less bug prone.
);
my #any_array = %some_hash; # Hash is expanded into a list of key/value pairs.
print "$_ => $some_hash{$_}\n"
for keys %some_hash;
print "\n\n", # You can print multiple newlines in one string.
"#any_array\n\n"; # print takes a list of things to print.
# In print #foo; #foo is expanded into a list of items to print.
# There is no separator between the members of #foo in the output.
# However print "#foo"; interpolates #foo into a string.
# It inserts spaces between the members of the arrays.
# This is the block form of 'for'
for my $k (sort keys %some_hash)
{
# Interpolating the variables into a string makes it easier to read the output.
print "$k => $some_hash{$k}\n";
}
Hashes provide unordered, access to data by a string key.
Arrays provide access to ordered data. Random access is available by using a numerical index.
If you need to preserve the order of a group of values, use an array. If you need to look up members of the group by an associated name, use a hash.
If you need to do both, you can use both structures together:
# Keep an array of sorted hash keys.
my #sorted_items = qw( first second third fourth );
# Store the actual data in the hash.
my %item;
#item{ #sorted_items } = 1..4; # This is called a hash slice.
# It allows you to access a list of hash elements.
# This can be a very powerful way to work with hashes.
# random access
print "third => $item{third}\n";
# When you need to access the data in order, iterate over
# the array of sorted hash keys. Use the keys to access the
# data in the hash.
# ordered access
for my $name ( #sorted_items ) {
print "$name => $item{$name}\n";
}
Looking at your code samples, I see a couple of things you might want to work on.
how looping structures like for and while can be used to reduce repeated code.
how to use variable interpolation
BTW, I am glad to see you working on basics and improving your code quality. This investment of time will pay off. Keep up the good work.
The elements are (almost certainly) printed out in the order they appear (internally) in the hash table itself -- i.e. based on the hash values of their keys.
The general rule to follow is to use something other than a hash table if you care much about the order.
Hashes are not (necessarily) retrieved in a sorted manner. If you want them sorted, you have to do it yourself:
use strict;
use warnings;
my %hash = ("a" => 1, "b" => 2, "c" => 3, "d" => 4);
for my $i (sort keys %hash) {
print "$i -> $hash{$i}\n";
}
You retrieve all the keys from a hash by using keys and you then sort them using sort. Yeah, I know, that crazy Larry Wall guy, who would've ever thought of calling them that? :-)
This outputs:
a -> 1
b -> 2
c -> 3
d -> 4
For most practical purposes, the order in which a hash table (not just Perl hash variables, but hash tables in general) can be considered random.
In reality, depending on the hashing implementation, the order may actually be deterministic. (i.e., If you run the program multiple times putting the same items into the hash table in the same order each time, they'll be stored in the same order each time.) I know that Perl hashes used to have this characteristic, but I'm not sure about current versions. In any case, hash key order is not a reliable source of randomness to use in cases where randomness is desirable.
Short version, then:
Don't use a hash if you care about the order (or lack of order). If you want a fixed order, it will be effectively random and if you want a random order, it will be effectively fixed.
A hash defines no ordering properties. The order in which things come out will be unpredictable.
And if you are crazy and have no duplicate values in your hash, and you need the values sorted, you can call reverse on it.
my %hash = ("a" => 1, "b" => 2, "c" => 3, "d" => 4);
my %reverse_hash = reverse %hash;
print $_ for sort keys %reverse_hash;
Caveat is the unique values part, duplicates will be overwritten and only one value will get in.

What decides the order of keys when I print a Perl hash?

activePerl 5.8 based
#!C:\Perl\bin\perl.exe
use strict;
use warnings;
# declare a new hash
my %some_hash;
%some_hash = ("foo", 35, "bar", 12.4, 2.5, "hello",
"wilma", 1.72e30, "betty", "bye\n");
my #any_array;
#any_array = %some_hash;
print %some_hash;
print "\n";
print #any_array;
print "\n";
print $any_array[0];
print "\n";
print $any_array[1];
print "\n";
print $any_array[2];
print "\n";
print $any_array[3];
print "\n";
print $any_array[4];
print "\n";
print $any_array[5];
print "\n";
print $any_array[6];
print "\n";
print $any_array[7];
print "\n";
print $any_array[8];
print "\n";
print $any_array[9];
Output as this
D:\learning\perl>test.pl
bettybye
bar12.4wilma1.72e+030foo352.5hello
bettybye
bar12.4wilma1.72e+030foo352.5hello
betty
bye
bar
12.4
wilma
1.72e+030
foo
35
2.5
hello
D:\learning\perl>
What decided the elements print order in my sample code?
Any rule to follow when print a mixed(strings, numbers) hash in Perl? Thank you.
bar12.4wilma1.72e+030foo352.5hello
[Updated]
With you guys help, i updated the code as below.
#!C:\Perl\bin\perl.exe
use strict;
use warnings;
# declare a new hash
my %some_hash;
%some_hash = ("foo", 35, "bar", 12.4, 2.5, "hello",
"wilma", 1.72e30, "betty", "bye");
my #any_array;
#any_array = %some_hash;
print %some_hash;
print "\n";
print "\n";
print #any_array;
print "\n";
print "\n";
my #keys;
#keys = keys %some_hash;
for my $k (sort #keys)
{
print $k, $some_hash{$k};
}
output
D:\learning\perl>test.pl
bettybyebar12.4wilma1.72e+030foo352.5hello
bettybyebar12.4wilma1.72e+030foo352.5hello
2.5hellobar12.4bettybyefoo35wilma1.72e+030
D:\learning\perl>
Finially, after called keys and sort functions. The hash keys print followed the rule below
2.5hellobar12.4bettybyefoo35wilma1.72e+030
Elements of a hash are printed out in their internal order, which can not be relied upon and will change as elements are added and removed. If you need all of the elements of a hash in some sort of order, sort the keys, and use that list to index the hash.
If you are looking for a structure that holds its elements in order, either use an array, or use one of the ordered hash's on CPAN.
the only ordering you can rely upon from a list context hash expansion is that key => value pairs will be together.
From perldoc -f keys:
The keys of a hash are returned in an apparently random order. The actual random order is subject to change in future versions of Perl, but it is guaranteed to be the same order as either the values or each function produces (given that the hash has not been modified). Since Perl 5.8.1 the ordering is different even between different runs of Perl for security reasons (see Algorithmic Complexity Attacks in perlsec).
...
Perl has never guaranteed any ordering of the hash keys, and the ordering has already changed several times during the lifetime of Perl 5. Also, the ordering of hash keys has always been, and continues to be, affected by the insertion order.
Also note that while the order of the hash elements might be randomised, this "pseudoordering" should not be used for applications like shuffling a list randomly (use List::Util::shuffle() for that, see List::Util, a standard core module since Perl 5.8.0; or the CPAN module Algorithm::Numerical::Shuffle), or for generating permutations (use e.g. the CPAN modules Algorithm::Permute or Algorithm::FastPermute), or for any cryptographic applications.
Note: since you are evaluating a hash in list context, you are at least guaranteed that each key is followed by its corresponding value; e.g. you will never see an output of a 4 b 3 c 2 d 1.
I went over your code and made some notes that I think you will find helpful.
use strict;
use warnings;
# declare a new hash and initialize it at the same time
my %some_hash = (
foo => 35, # use the fat-comma or '=>' operator, it quotes the left side
bar => 12.4,
2.5 => "hello",
wilma => 1.72e30,
betty => "bye", # perl ignores trailing commas,
# the final comma makes adding items to the end of the list less bug prone.
);
my #any_array = %some_hash; # Hash is expanded into a list of key/value pairs.
print "$_ => $some_hash{$_}\n"
for keys %some_hash;
print "\n\n", # You can print multiple newlines in one string.
"#any_array\n\n"; # print takes a list of things to print.
# In print #foo; #foo is expanded into a list of items to print.
# There is no separator between the members of #foo in the output.
# However print "#foo"; interpolates #foo into a string.
# It inserts spaces between the members of the arrays.
# This is the block form of 'for'
for my $k (sort keys %some_hash)
{
# Interpolating the variables into a string makes it easier to read the output.
print "$k => $some_hash{$k}\n";
}
Hashes provide unordered, access to data by a string key.
Arrays provide access to ordered data. Random access is available by using a numerical index.
If you need to preserve the order of a group of values, use an array. If you need to look up members of the group by an associated name, use a hash.
If you need to do both, you can use both structures together:
# Keep an array of sorted hash keys.
my #sorted_items = qw( first second third fourth );
# Store the actual data in the hash.
my %item;
#item{ #sorted_items } = 1..4; # This is called a hash slice.
# It allows you to access a list of hash elements.
# This can be a very powerful way to work with hashes.
# random access
print "third => $item{third}\n";
# When you need to access the data in order, iterate over
# the array of sorted hash keys. Use the keys to access the
# data in the hash.
# ordered access
for my $name ( #sorted_items ) {
print "$name => $item{$name}\n";
}
Looking at your code samples, I see a couple of things you might want to work on.
how looping structures like for and while can be used to reduce repeated code.
how to use variable interpolation
BTW, I am glad to see you working on basics and improving your code quality. This investment of time will pay off. Keep up the good work.
The elements are (almost certainly) printed out in the order they appear (internally) in the hash table itself -- i.e. based on the hash values of their keys.
The general rule to follow is to use something other than a hash table if you care much about the order.
Hashes are not (necessarily) retrieved in a sorted manner. If you want them sorted, you have to do it yourself:
use strict;
use warnings;
my %hash = ("a" => 1, "b" => 2, "c" => 3, "d" => 4);
for my $i (sort keys %hash) {
print "$i -> $hash{$i}\n";
}
You retrieve all the keys from a hash by using keys and you then sort them using sort. Yeah, I know, that crazy Larry Wall guy, who would've ever thought of calling them that? :-)
This outputs:
a -> 1
b -> 2
c -> 3
d -> 4
For most practical purposes, the order in which a hash table (not just Perl hash variables, but hash tables in general) can be considered random.
In reality, depending on the hashing implementation, the order may actually be deterministic. (i.e., If you run the program multiple times putting the same items into the hash table in the same order each time, they'll be stored in the same order each time.) I know that Perl hashes used to have this characteristic, but I'm not sure about current versions. In any case, hash key order is not a reliable source of randomness to use in cases where randomness is desirable.
Short version, then:
Don't use a hash if you care about the order (or lack of order). If you want a fixed order, it will be effectively random and if you want a random order, it will be effectively fixed.
A hash defines no ordering properties. The order in which things come out will be unpredictable.
And if you are crazy and have no duplicate values in your hash, and you need the values sorted, you can call reverse on it.
my %hash = ("a" => 1, "b" => 2, "c" => 3, "d" => 4);
my %reverse_hash = reverse %hash;
print $_ for sort keys %reverse_hash;
Caveat is the unique values part, duplicates will be overwritten and only one value will get in.

How can I combine hashes in Perl?

What is the best way to combine both hashes into %hash1? I always know that %hash2 and %hash1 always have unique keys. I would also prefer a single line of code if possible.
$hash1{'1'} = 'red';
$hash1{'2'} = 'blue';
$hash2{'3'} = 'green';
$hash2{'4'} = 'yellow';
Quick Answer (TL;DR)
%hash1 = (%hash1, %hash2)
## or else ...
#hash1{keys %hash2} = values %hash2;
## or with references ...
$hash_ref1 = { %$hash_ref1, %$hash_ref2 };
Overview
Context: Perl 5.x
Problem: The user wishes to merge two hashes1 into a single variable
Solution
use the syntax above for simple variables
use Hash::Merge for complex nested variables
Pitfalls
What do to when both hashes contain one or more duplicate keys
(see e.g., Perl - Merge hash containing duplicate keys)
(see e.g., Perl hashes: how to deal with duplicate keys and get possible pair)
Should a key-value pair with an empty value ever overwrite a key-value pair with a non-empty value?
What constitutes an empty vs non-empty value in the first place? (e.g. undef, zero, empty string, false, falsy ...)
See also
PM post on merging hashes
PM Categorical Q&A hash union
Perl Cookbook 5.10. Merging Hashes
websearch://perlfaq "merge two hashes"
websearch://perl merge hash
https://metacpan.org/pod/Hash::Merge
Footnotes
1 * (aka associative-array, aka dictionary)
Check out perlfaq4: How do I merge two hashes. There is a lot of good information already in the Perl documentation and you can have it right away rather than waiting for someone else to answer it. :)
Before you decide to merge two hashes, you have to decide what to do if both hashes contain keys that are the same and if you want to leave the original hashes as they were.
If you want to preserve the original hashes, copy one hash (%hash1) to a new hash (%new_hash), then add the keys from the other hash (%hash2 to the new hash. Checking that the key already exists in %new_hash gives you a chance to decide what to do with the duplicates:
my %new_hash = %hash1; # make a copy; leave %hash1 alone
foreach my $key2 ( keys %hash2 )
{
if( exists $new_hash{$key2} )
{
warn "Key [$key2] is in both hashes!";
# handle the duplicate (perhaps only warning)
...
next;
}
else
{
$new_hash{$key2} = $hash2{$key2};
}
}
If you don't want to create a new hash, you can still use this looping technique; just change the %new_hash to %hash1.
foreach my $key2 ( keys %hash2 )
{
if( exists $hash1{$key2} )
{
warn "Key [$key2] is in both hashes!";
# handle the duplicate (perhaps only warning)
...
next;
}
else
{
$hash1{$key2} = $hash2{$key2};
}
}
If you don't care that one hash overwrites keys and values from the other, you could just use a hash slice to add one hash to another. In this case, values from %hash2 replace values from %hash1 when they have keys in common:
#hash1{ keys %hash2 } = values %hash2;
This is an old question, but comes out high in my Google search for 'perl merge hashes' - and yet it does not mention the very helpful CPAN module Hash::Merge
For hash references. You should use curly braces like the following:
$hash_ref1 = {%$hash_ref1, %$hash_ref2};
and not the suggested answer above using parenthesis:
$hash_ref1 = ($hash_ref1, $hash_ref2);