# FunctionIndices

A small package allows indexing array with functions via a simple wrapper FI. For example, A[FI(iseven)] returns an array containing all elements of A whose indices instead of values are even, like (0:3)[FI(iseven)] == [1, 3]. To access elements whose values are even, try filter(iseven, A). As a special case, for indexing with !=(i) or !in(I), which are expected to get elements whose index are not is i or not in I, there is another wrapper not providing a convenient and optimized way. The not is similar to Not of InvertedIndices, but faster in some cases, see performance comparing for more information.

## Quick start to index with function index

1-d indexing A[FI(f)] is equivalent to A[map(f, begin:end)], multidimensional indexing A[FI(f1), ..., FI(fn)] is equivalent to A[map(FI(f1), axes(A, 1)), ..., map(FI(fn), axes(A, n))].

julia> using FunctionIndices

julia> A = reshape(0:11, 3, 4)
3×4 reshape(::UnitRange{Int64}, 3, 4) with eltype Int64:
0  3  6   9
1  4  7  10
2  5  8  11

julia> A[FI(iseven)]
6-element Vector{Int64}:
1
3
5
7
9
11

julia> A[map(iseven, begin:end)]
6-element Vector{Int64}:
1
3
5
7
9
11

julia> A[FI(isodd), FI(iseven)]
2×2 Matrix{Int64}:
3   9
5  11

julia> A[map(isodd, begin:end), map(iseven, begin:end)]
2×2 Matrix{Int64}:
3   9
5  11

not is an alternative to Not, and in most cases they are equivalent:

julia> using InvertedIndices

julia> A[not(1)] == A[Not(1)]
true

julia> A[not(1, 2)] == A[Not(1, 2)]
true

julia> A[not(1:2)] == A[Not(1:2)]
true

julia> let I = rand(Bool, size(A)); A[not(I)] == A[Not(I)] end
true

But for CartesianIndex and CartesianIndices, A[not(CartesianIndex(i, j,...))] is equivalent to A[not(i), not(j), ...] and A[not(CartesianIndices((I, J,...))] is equivalent to A[not(I), not(J), ...], where not treats inverted Cartesian indices as Cartesian inverted indices, and always returns an array with the same dimension. However, A[Not(CartesianIndex(i, j,...))] converts CartesianIndex to linear index and return a vector, and A[Not(CartesianIndices((I, J,...)))] seems an undefined behaviour.

julia> A[not(CartesianIndex(1, 2))] # equivalent to A[not(1), not(2)]
2×3 Matrix{Int64}:
1  7  10
2  8  11

julia> A[Not(CartesianIndex(1, 2))] # equivalent to A[Not(3)]
11-element Vector{Int64}:
0
1
2
4
5
⋮
8
9
10
11

julia> A[not(CartesianIndex(1, 2):CartesianIndex(2, 3))] # equivalent to A[not(1:2), not(2:3)]
1×2 Matrix{Int64}:
2  11

julia> A[Not(CartesianIndex(1, 2):CartesianIndex(2, 3))] # seems an undefined behavior
1×2 Matrix{Int64}:
5  8

Besides, for out of bounds index like A[4, 5], A[not(4), not(5)] is equivalent to A[:, :], because inbounds indices are not equal to the given value, while A[Not[4], Not(5)] throws a BoundsError.

This package is also compatible with OffsetArrays:

julia> using OffsetArrays

julia> OA = OffsetArray(A, 2:4, 0:3)
3×4 OffsetArray(reshape(::UnitRange{Int64}, 3, 4), 2:4, 0:3) with eltype Int64 with indices 2:4×0:3:
0  3  6   9
1  4  7  10
2  5  8  11

julia> OA[FI(iseven), FI(iseven)] # OA[[2, 4], [0, 2]]
2×2 Matrix{Int64}:
0  6
2  8

julia> OA[not(2), not(3)] # OA[[3, 4], [0, 1, 2]]
2×3 Matrix{Int64}:
1  4  7
2  5  8