Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I return multiple Pairs from a map feeding into a hash?

I'm trying to create a hash that looks up by key or value. To look up both ways, I want the values to be keys (with values that are their keys).

<
H He
>
    ==> map( {
        state $n = 0; $n++;
        $n     => $_.Str,
        $_.Str => $n
         } )
    ==> my %elements;

%elements.perl.say;

%elements{2}.WHAT.say;

This gives me this weird thing:

{"1\tH H\t2" => $(2 => "He", :He(2))}
(Any)

With only one pair, it's fine:

<
H He
>
    ==> map( {
        state $n = 0; $n++;
        $n     => $_.Str
         } )
    ==> my %elements;

%elements.perl.say;

%elements{2}.WHAT.say;

Now I get what I want, but without the reverse pair:

{"1" => "H", "2" => "He"}
(Str)
like image 404
brian d foy Avatar asked Sep 03 '16 14:09

brian d foy


1 Answers

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 Pairs:

<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)

like image 109
smls Avatar answered Sep 21 '22 09:09

smls