I have a multi-dimensional hash in perl and I would like to change the first key for a chosen value. For example, I have the hash
my %Hash1;
$Hash1{1}{12}=1;
$Hash1{1}{10}=1;
$Hash1{2}{31}=1;
$Hash1{3}{52}=1;
$Hash1{3}{58}=1;
$Hash1{4}{82}=1;
$Hash1{4}{154}=1;
Now I want to replace the value 3 in the first key with the value 300. After this I would get:
$Hash1{1}{12}=1;
$Hash1{1}{10}=1;
$Hash1{2}{31}=1;
$Hash1{300}{52}=1;
$Hash1{300}{58}=1;
$Hash1{4}{82}=1;
$Hash1{4}{154}=1;
I know I could create a new hash by scanning the original hash and doing the following:
my %Hash2;
foreach my $key1 (sort keys %Hash1) {
foreach my $key2 (keys %{ $Hash1{$key1} }) {
if($key1==3){
$Hash2{300}{$key2}=1;
} else {
$Hash2{$key1}{$key2}=1;
}
}
}
But is there a quicker way?
$Hash1{300} = $Hash1{3};
delete $Hash1{3};
Related
I have a hash that looks like this:
my $hash = {
level1_f1 => {
level2_f1 => 'something',
level2_f2 => 'another thing'
},
level1_f2 => {
level2_f3 => 'yet another thing',
level2_f4 => 'bla bla'
level2_f5 => ''
}
...
}
I also got a list of values that correspond to the "level2" keys, which I want to know if thy exist in the hash.
#list = ("level2_f2", "level2_f4", "level2_f99")
I don't know which "level1" key each element of #list belongs to. The only way of finding if they existed I could think was using a foreach loop to go through #list, another foreach loop to go through the keys of %hash and checking
foreach my $i (#array) {
foreach my $k (keys %hash) {
if (exists $hash{$k}{$list[$i]})
}
}
but I wanted to know if there is a more eficient or maybe a more elegant way to do it. All the answers I found ask you to know the "level1" key, which I don't.
Thanks!!
Use values:
for my $inner_hash (values %$hash) {
say grep exists $inner_hash->{$_}, #list;
}
You have to loop all the level1 keys. But if you don't need to know which keys match and merely care for the existence of any, then you don't have to ask for each member of your list explicitly. You could say
foreach my $k (keys %hash) {
if ( #{ $hash{$k} }{ #list } )
{
}
}
The hash slice will return all values in the subhash which have matching keys in the list. Keys in the list that are not in the subhash get ignored.
Note however, that this does potentially more work than you may really need.
You don't need to iterate over "the entire hash".
You will necessarily over the elements of the outer hash since you want to check the value of each one, but you don't need to iterate over the elements of the inner hashes. Your solution already demonstrates that.
So your solution is as efficient as it can be, at least in terms of how well it scales. You can only perform small optimizations such as stopping as soon as a match is found.
for my $i (#list) {
while ( my (undef, $inner) = each(%hash) ) {
if (exists($inner->{$i}) {
...
last;
}
}
keys(%hash); # Reset iterator since it might not be exhausted.
}
As a micro optimization, it might be beneficial to invert the nesting of the loops.
my %list = map { $_ => 1 } #list;
while ( my (undef, $inner) = each(%hash) ) {
while (defined( my $k = each(%$inner) )) {
if ($list{$k}) {
delete($list{$k});
...
last if !keys(%list);
}
}
keys(%$inner); # Reset iterator since it might not be exhausted.
last if !keys(%list);
}
keys(%hash); # Reset iterator since it might not be exhausted.
If the hashes are small, these changes might actually slow things down.
Honestly, if there's truly a speed issue, the problem is that you used the wrong data structure for the type of query you want to run on it!
my %number_words = hash_a_file($_);
foreach my $key ( keys %number_words ) {
++$word_list{$key};
}
This is working but I would like to avoid using the intermediate variable
like this
foreach my $key ( keys hash_a_file($_) ) {
++$word_list{$key};
}
I tried to use refs but still failed. Any way to do this ? Thanks !
The thing is, a subroutine doesn't return a hash. It returns a list. In your original code, it only becomes a hash when you store it in a hash variable.
But there are other ways to create a hash from a list. You can create an anonymous hash and then de-reference it. It's ugly, but it works.
# Inner braces create an anonymous hash.
# Outer braces de-reference that into a "real" hash
foreach my $key ( keys %{ { hash_a_file($_) } } ) {
++$word_list{$key};
}
Update: To back up Borodin's comment, I should add that if this code was presented to me in a code review, I'd suggest rewriting it to use an explicit hash variable as your original code does.
Return a hashref, so that you can form a valid argument for keys (instead of a list)
sub hash_a_file { return { a => 1, b => 2 } }
foreach my $key ( keys %{ hash_a_file() } ) {
say $key
}
I am tracing a Perl script and I cannot understand what the following expression is supposed to do:
keys %{ $data->{net_assets_detail}->{$port}->{$manager} }
I am trying to understand which key from which hash we are trying to access — from data, port or manager?
Below is the complete code:
foreach my $port ( keys %{$data->{net_assets_detail} } ) {
foreach my $manager (keys %{ $data->{net_assets_detail}->{$port} } ) {
my $fund_value = MOA::CLSUtils::get_manager_fund_value( $port, $manager, $args->{end_date} );
$fund_value ||=0;
my $net_asset_value = $data->{net_assets_manager}->{$port}->{$manager};
$net_asset_value ||=0;
foreach my $tran_type (keys %{ $data->{net_assets_detail}->{$port}->{$manager} } ) {
my $value = $data->{net_assets_detail}->{$port}->{$manager}->{$tran_type};
print OUT"$port\t";
print OUT"$manager\t";
print OUT"$tran_type\t";
print OUT"$value\n";
}
}
}
}
Let's look at the line part by part.
%{ $data->{net_assets_detail}->{$port}->{$manager} }
There is a hashref called $data.
Inside, there are some keys. One of them is net_assets_detail
Inside is a hashref
Inside, there are some keys. One of them is the value of $port
Inside is a hashref
Inside, there are some keys. One of them is the value of $manager
The %{ ... } is dereferencing the hashref, so built-ins that expect a hash can work on it.
The keys takes all the keys of the hash (which was dereferenced from the long thingy) and returns them as a list. The foreach iterates over that list, and puts each key into the lexical variable $tram_type that is available in the body of the loop.
I have this hash with hashses.
I would like to iterate only the value of '0'.
$VAR1 = {
'1' => {
'192.168.1.1' => '192.168.1.38'
},
'0' => {
'192.168.32.6' => '192.168.32.43'
}
};
The only way I can access it is by creating two foreach my $key (keys(%myhash)) loops:
Can I use:
foreach my $key (keys(%myhash{0})) ## does not work
or directly access the values somehow?
Thanks
First of all, if you are using consecutive integers as keys to a hash then it is likely that you should be using an array instead.
The value of the hash corresponding to key 0 is $dhcpoffers{0} because it is a scalar value. %dhcpoffers{0} is just a syntax error.
You need
for my $key (keys %{ $dhcpoffers{0} }) { ... }
or, if you prefer
my $offer_0 = $dhcpoffers{0};
for my $key (keys %$offer_0) { ... }
Since version 14 of Perl 5, keys will accept a hash reference, so you may be able to write the much cleaner
for my $key (keys $dhcpoffers{0}) { ... }
I need to print keys based on vales in hash. Here is the code, I wrote
foreach $value (values %hash)
{
print "$value\t$hash{$value}\n";
}
Error: I can only print values, but not keys.
Any help will be greatly appreciated.
Thanks
Hashes are designed to be accessed by key, not by value. You need to loop over a list of keys, not values.
Then you can use the keys to access the associated values.
foreach my $key (keys %hash) {
my $value = $hash{$key};
say "$key = \t$value";
}
Try with:
for my $key (keys %hash) {
print "$key\t$hash{$key}\n";
}
print "$_\t$hash{$_}\n" for keys %hash;
One-liner:
map { print "$_\t$hash{$_}\n" } keys %hash;
I would probably use while and each if you want to iterate through keys and values:
while (my ($key, $value) = each %hash) {
say "$key -> $value";
}
The title requests to print key based on the value.
if your key and value in your harsh table should be one to one
foreach $key (keys %hash)
{
$r_hash{$hash{$key}}=$key;
}
....
if you want to access it by values, then define your hash as
$x = { 'x1' => [ 'one','x1']}
foreach ( values %$x )
{
foreach $m1 (#$_) {
print "$m1\n";
}
}
Notice you can get the key from value by second member of the value array.