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

2

13

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!

Sewn answered 7/1 at 3:23 Comment(2)
Do you mean "Fourth" for the fourth element?Coenzyme
I used "first" on purpose (instead of "fourth") to indicate that this is an arbitrary name.Sewn
H
15

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.

Horizontal answered 7/1 at 9:24 Comment(0)
C
2

Using for (and/or given) loops to get Array/index output:

~$ raku -e 'my @a = "First", "Second", "Third", "Fourth";  \
            given @a.kv { .put };'
0 First 1 Second 2 Third 3 Fourth
~$ raku -e 'my @a = "First", "Second", "Third", "Fourth";  \
            for @a.kv { .put };'
0
First
1
Second
2
Third
3
Fourth
~$ raku -e 'my @a = "First", "Second", "Third", "Fourth";  \
            for @a.kv -> $k,$v { say $k => $v };'
0 => First
1 => Second
2 => Third
3 => Fourth
~$ raku -e 'my @a = "First", "Second", "Third", "Fourth";  \
            for @a { put "Index of {$_.values} is " ~ $++ ~ ";" };'
Index of First is 0;
Index of Second is 1;
Index of Third is 2;
Index of Fourth is 3;
~$ raku -e 'my @a = "First", "Second", "Third", "Fourth";  \
            for @a.kv -> $k,$v { put "Index of $v is $k;" };'
Index of First is 0;
Index of Second is 1;
Index of Third is 2;
Index of Fourth is 3;
~$ raku -e 'my @a = "First", "Second", "Third", "Fourth";  \
            for @a.pairs { put "Index of {$_.value} is {$_.key};" };'
Index of First is 0;
Index of Second is 1;
Index of Third is 2;
Index of Fourth is 3;

If you've read this far, you're probably wondering how this relates to the original map question. The answer is simple: add an imperative do to the for/given loop, drop the put, and now you have the same (stringified) return, just as if you were using map instead:

~$ raku -e 'my @a = "First", "Second", "Third", "Fourth";  \
            my @b = do for @a.pairs { "Index of {$_.value} is {$_.key};" };  \
            .put for @b;'
Index of First is 0;
Index of Second is 1;
Index of Third is 2;
Index of Fourth is 3;
~$ raku -e 'my @a = "First", "Second", "Third", "Fourth";  \
            @a = do for @a.pairs { "Index of {$_.value} is {$_.key};" };  \
           .put for @a;'
Index of First is 0;
Index of Second is 1;
Index of Third is 2;
Index of Fourth is 3;

Yet another trick in the Raku grab-bag of tricks!

https://docs.raku.org/syntax/do%20%28statement%20prefix%29

HTH.

Coenzyme answered 9/1 at 1:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.