Eachindex for individual axes of multidimensional arrays?

Because arrays’ indices don’t generally start from 1, best practice is to avoid anything like “for i in 1:size()” and instead use eachindex. But for multidimensional arrays, eachindex iterates over all elements of the array, and sometimes you want to iterate only over some subset of its dimensions. (For instance: if you have a sequence of points in the plane, represented by a 2-dimensional array. Iterating over all the points means iterating over just one of the dimensions.)

If I understand correctly, the approved way to do this is something like

for i in firstindex(A,2):lastindex(A,2)
    x,y = A(1,i),A(2,i)
    # ... do something with x,y ...
end

but this is a bit cumbersome. (And when something is cumbersome, people will tend to do something less cumbersome instead even if it isn’t quite right. I bet there are a lot of loops of this sort that go from 1 to size(A,whatever). And all those loops are wrong if it is possible for A to be an OffsetArray.)

I suggest that eachindex should take an optional second argument, a dimension number; obviously this should only succeed when the first argument is an AbstractArray rather than, say, a dictionary. So the loop above would become

for i in eachindex(A,2)
    x,y = A(1,i),A(2,i)
    # ... do something with x,y ...
end

eachindex can already take multiple arguments; they are all expected to be AbstractArrays of the same size and eachindex will return an indices object that works with them all. My proposal would give another meaning to calling eachindex with multiple arguments. Unless I am missing something there is no case in which it would give a new meaning to anything that was not previously illegal.

1 Like

Iterate over the axes of the second dimension instead:

for i in axes(A, 2)
6 Likes

I know this is a hot topic these days but we should calm down on it. Julia IS a 1 based language and therefore indices generally start from 1. Cursing 1:length(whatever) doesn’t look a good thing for me.

1 Like

Oh, good call. I somehow failed to see that, which I agree does everything I was after. May I suggest that the documentation for eachindex should maybe point at axes to make this easier to find?

1 Like

Sorry, my wording was suboptimal. “don’t generally” meant “don’t in full generality” or something, and it would have been better to say “don’t always”; of course I didn’t mean to say that indices don’t usually start from 1.

But it is Just True that indices don’t always start from 1, and using 1:length() is clearly worse than using eachindex.

I echo this, axis has See also: size, keys, eachindex.

eachindex should also include a See also: axis, …

1 Like

See how this looks: See also `axes` from `eachindex`'s docstring, etc. by mcabbott · Pull Request #45356 · JuliaLang/julia · GitHub

4 Likes

Looks good to me!