Search code examples
dictionaryraku

Is it possible to use the index of an array in a map block?


I would like to use the index of the array in a map routine. For example, this Raku code:

raku -e 'my @a = "First", "Second", "Third", "First"; say @a.map({ "Index of $_ is: ;" })'

prints:

(Index of First is: ; Index of Second is: ; Index of Third is: ; Index of First is: ;)

Would it be possible to get the index of the array, like:

(Index of First is: 0; Index of Second is: 1; Index of Third is: 2; Index of First is: 3;)

Thanks!


Solution

  • There is .kv to produce "keys" and values. For an array, the keys are the indexes 0, 1, 2...

     >>> my @a = "First", "Second", "Third", "First";
    [First Second Third First]
    
    >>> @a.kv
    (0 First 1 Second 2 Third 3 First)
    

    This is a flat sequence of length 2N; we can tell map to take 2 things at a time, either via an explicit signature:

    >>> @a.kv.map(-> $index, $value { "Index of $value is $index;" })
    (Index of First is 0; Index of Second is 1; Index of Third is 2; Index of First is 3;)
    

    or via placeholder variables:

    >>> @a.kv.map({ "Index of $^value is $^index;" })
    (Index of First is 0; Index of Second is 1; Index of Third is 2; Index of First is 3;)
    

    Since index comes lexicographically before value, it works out as in the previous explicit signature case, i.e., the same signature is produced for us behind the scenes. We can see this:

    >>> &{ $^value, $^index }
    -> $index, $value { #`(Block|140736965530488) ... }
    

    Note that what matters is the Unicode order of variable names, not the order of appearance.


    In the spirit of TIMTOWDI, some alternatives (IMO not better):

    # Limitation: generated anew per closure and per mention,
    # so cannot use, e.g., in string interpolations with closure and more than once
    >>> @a.map({ "Index of $_ is " ~ $++ ~ ";" })
    (Index of First is 0; Index of Second is 1; Index of Third is 2; Index of First is 3;)
    
    • Using .pairs, cousine of .kv to produce "key => value" pairs instead of a flat list
    >>> @a.pairs
    (0 => First 1 => Second 2 => Third 3 => First)
    
    # Reach over .key & .value
    >>> @a.pairs.map({ "Index of $_.value() is $_.key();" })
    (Index of First is 0; Index of Second is 1; Index of Third is 2; Index of First is 3;)
    
    # Unpack with signature
    >>> @a.pairs.map(-> (:key($index), :$value) { "Index of $value is $index;" })
    (Index of First is 0; Index of Second is 1; Index of Third is 2; Index of First is 3;)
    
    • Instead of mapping the array's values themselves, we can map the keys of it:
    # Array.keys produces 0, 1, 2...
    >>> @a.keys.map({ "Index of @a[$_] is $_;" })
    (Index of First is 0; Index of Second is 1; Index of Third is 2; Index of First is 3;)
    
    # ^@a is equivalent to ^@a.elems === 0 ..^ @a.elems, i.e., 0, 1, 2... again
    >>> ^@a .map({ "Index of @a[$_] is $_;" })
    (Index of First is 0; Index of Second is 1; Index of Third is 2; Index of First is 3;)
    

    This last one is the least idiomatic I think.