Given the indexer wants the elements to extract, we could solve this by turning the list of elements to exclude into a list of ranges of elements to extract. That is, given:
1, 3, 5
We'd produce something equivalent to:
0..0, 2..2, 4..4, 6..Inf
Given:
my @exclude = 1, 3, 5;
We can do:
-1, |@exclude Z^..^ |@exclude, Inf
To break it down, zip (-1, 1, 3, 5)
with (1, 3, 5, Inf)
, but using the range operator with exclusive endpoints. This results in, for the given example:
(-1^..^1 1^..^3 3^..^5 5^..^Inf)
Which is equivalent to the ranges I mentioned above. Then we stick this into the indexer:
my @a = <a b c d e f g>
my @exclude = 1, 3, 5;
say @a[-1, |@exclude Z^..^ |@exclude, Inf].flat
Which gives the desired result:
(a c e g)
This approach is O(n + m). It will probably work out quite well if there array is long, but the number of things to exclude is comparatively small, since it only produces the Range
objects needed for the indexing, and then indexing by range is relatively well optimized.
Finally, should the flat
on the outside be considered troublesome, it's also possible to move it inside:
@a[{ flat -1, |@exclude Z^..^ |@exclude, $_ }]
Which works because the block is passed the number of elements in @a
.