Multiply all values in a %hash and return a %hash with the same structure

Firstly, there is an issue with logic of your code. Initially, you are getting a hash of values, "feet": "6'07\"", "meters": 2.0 parsed out of json, with meters being a number and feet being a string. Next, you are trying to multiply it on a random value... And while it will work for a number, it won't for a string. Perl 6 allomorphs allow you to do that, actually: say "5" * 3 will return 15, but X"Y' pattern is complex enough for Perl 6 to not naturally understand it.

So you likely need to convert it before processing, and to convert it back afterwards.

The second thing is exact line that leads to the error you are observing.

Consider this:

my %a = a => 5;
%a = %a * 10 => 5; # %a becomes a hash with a single value of 10 => 5
# It happens because when a Hash is used in math ops, its size is used as a value
# Thus, if you have a single value, it'll become 1 * 10, thus 10
# And for %a = a => 1, b => 2; %a * 5 will be evaluated to 10
%a = %a * 10; # error, the key is passed, but not a value

To work directly on hash values, you want to use map method and process every pair, for example: %a .= map({ .key => .value * (0.9..1.1).rand }).

Of course, it can be golfed or written in another manner, but the main issue is resolved this way.


You've accepted @Takao's answer. That solution requires manually digging into %hash to get to leaf hashes/lists and then applying map.

Given that your question's title mentions "return ... same structure" and the body includes what looks like a nested structure, I think it's important there's an answer providing some idiomatic solutions for automatically descending into and duplicating a nested structure:

my %hash = :a{:b{:c,:d}}

say my %new-hash = %hash».&{ (0.9 .. 1.1) .rand }
# {a => {b => {c => 1.0476391741359872, d => 0.963626602773474}}}

# Update leaf values of original `%hash` in-place:
%hash».&{ $_ = (0.9 .. 1.1) .rand }

# Same effect:
%hash »*=» (0.9..1.1).rand;

# Same effect:
%hash.deepmap: { $_ = (0.9..1.1).rand }

Hyperops (eg ») iterate one or two data structures to get to their leaves and then apply the op being hypered:

say %hash».++ # in-place increment leaf values of `%hash` even if nested

.&{ ... } calls the closure in braces using method call syntax. Combining this with a hyperop one can write:

%hash».&{ $_ = (0.9 .. 1.1) .rand }

Another option is .deepmap:

%hash.deepmap: { $_ = (0.9..1.1).rand }

A key difference between hyperops and deepmap is that the compiler is allowed to iterate data structures and run hyperoperations in parallel in any order whereas deepmap iteration always occurs sequentially.

Tags:

Raku

Rakudo