Your map
returns a sequence of List
values that each contain two Pair
values.
Feeding this to the hash won't automatically flatten it, and each List
becomes a key or value of the hash.
There are several ways to get it to do what you want:
Using flat
You can use the built-in flat
function to flatten the result of the map
, so that the hash receives a sequence of Pair
s:
<H He>
==> map {
(state $n)++;
+$n => ~$_,
~$_ => +$n
}\
==> flat()
==> my %elements;
%elements.say; # {1 => H, 2 => He, H => 1, He => 2}
%elements{2}.WHAT.say; # (Str)
Both map
and flat
return lazy sequences, so this is (in theory) memory-efficient even if the list of elements is large.
- Side note: I've also written
+$n
instead of $n
because the =>
Pair construction operator (like the ,
List construction operator) doesn't "decontainerize" its arguments - if you give it an item container such as a $
variable, it'll bind to that directly instead of the value contained in it, which causes the next $n++
to also affect the Pair
returned from the previous iteration. Giving the pair construction operator a value expression instead of a variable, avoids this.
Using slip
slip
is the inside-out version of flat
:
<H He>
==> map {
(state $n)++;
slip
+$n => ~$_,
~$_ => +$n
}\
==> my %elements;
Each iteration of the map
now returns a value of type Slip
, which is just like List
except that it automatically dissolved into any parent list it becomes part of.
Refactoring
If you're not tied to your particular approach, here are two potentially clearer ways to generate the same hash:
my @keys = 1..*;
my @values = <H He>;
my %elements = flat (@keys Z=> @values), (@values Z=> @keys);
Or:
my %elements = 1..* Z=> <H He>;
push %elements, %elements.invert;
(Z
is the "zip" meta-operator)