Is there a nice way to get a partial Array derived from another Array?
Example: given an array with x number of elements, and an Int $index
within the range of elements of that Array, I would like to run a function from $index + 1
to number of elements - 1.
Essentially I would like to compare all elements within the Array. Compare being a function of the element itself. (Note I am aware of the function eqv
, though this is not suitable for this particular situation)
I was thinking of something in the likes of:
for $!my-array.kv -> $index, $element
{
for $!my-array[$index+1 .. .elems-1] -> $another-element
{
$element.compare($another-element)
}
}
.elems - 1
calls a method on $_
, which is probably wrong.
If you are going to write it that way $!my-array.elems - 1
, or you could give it a lambda *.elems - 1
. The usual lambda is * - 1
(an Array numifies to the number of elements).
In this case I would just use a Whatever *
.
$!my-array[$index + 1 .. *]
To get for
to return a result, prefix it with do
.
my @result = do for $!my-array.kv -> $index, $element
{
do for $!my-array[$index + 1 .. *] -> $another-element
{
$element.compare($another-element)
}
}
It might be clearer to use map
$!my-array.kv.map: -> $index, $element {
$!my-array[$index + 1 .. *].map: -> $another-element {
$element.compare($another-element)
}
}
Since you mention that eqv
isn't suitable, that leads me to believe that .compare
returns True
when they are equivalent, and False
otherwise.
So then eqv
is actually suitable. You just have to adjust it first.
class Foo {
method compare (Foo:D: Foo:D $other ) {…}
…
}
# this gets mixed into the existing &infix:«eqv»
# mark it for export so that it is available in other code units
multi sub infix:«eqv» ( Foo:D \left, Foo:D \right ) is export {
left.compare(right)
}
# must import it everywhere you want the special cased &infix:«eqv»
# as operators are lexically scoped
use Foo;
$!my-array.kv.map: -> $index, $element {
# cross using &infix:«eqv»
$element X[eqv] $!my-array[$index + 1 .. *]
}
You could also use «eqv«
(<<eqv<<
).
What you wrote happens to coincide fairly closely with combinations
.
my @result = $!my-array.combinations(2).map(
-> ( $a, $b ) { $a eqv $b }
)
This produces a flat list.
< a b c d e >.combinations(2).map: -> ($element, $another-element){
$element cmp $another-element
}
# (Less,Less,Less,Less,Less,Less,Less,Less,Less,Less)
While the previous code produces a list of lists. (with an extra empty one)
my @a = < a b c d e >;
say @a.kv.map: -> $index, $value {
@a[$index ^.. *].map: $value cmp *
}
# (
# (Less,Less,Less,Less),
# (Less,Less,Less),
# (Less,Less),
# (Less,),
# (),
# )
Note that you can spell the name of the infix operator eqv
in several ways
&infix:« eqv »
&infix:< eqv >
&infix:[ 'eqv' ]
&[eqv]
All but the last one can be used when declaring a new multi sub
implementation.
(after removing the &
)
P.S. I think a method named .compare
should return an Order
(Less
,Same
,More
).
So then the correct operator to adjust would be &infix:«cmp»
.
You can avoid the double for
loop by use combinations
. Assuming you do not want to record the indices, it can be as simple as:
for @($!my-array).combinations: 2 -> ( $element, $another-element ) {
$element.compare($another-element);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With