Can we allow the syntax `v.[i]` to be converted to `getindex.(v, i)`?

Hi!

I have one scenario that happens all the time. I get a vector of vectors and I need to obtain each component individually to perform some analyses and plot the data. Today, I do:

getindex.(v, 1)
getindex.(v, 2)
getindex.(v, 3)
...

However, I am wondering if we can allow the following syntax:

v.[1]
v.[2]
v.[3]
...

It makes sense to me because we are broadcasting the get index operation.

What do you think?

3 Likes

see also What to do about nonscalar indexing? ¡ Issue #30845 ¡ JuliaLang/julia ¡ GitHub

4 Likes

Bear in mind this is different from OP’s intention to broadcast over the array as well:

julia> A = zeros(10,2);

julia> broadcast((idxs...)->A[idxs...], 1:10, 1) # ~getindex.((A,), 1:10, 1)
10-element Vector{Float64}:
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0

julia> getindex.(A, 1:10, 1)
ERROR: BoundsError
Stacktrace:
  [1] getindex
    @ ./number.jl:103 [inlined]

If I had to pick, I’d pick the former because it’s strange to think of a dotted instance as something that is iterated. I would prefer neither because it’s simpler to understand if we always dot the callable.

2 Likes

TLDR: there are three conflicting meanings to what A.[I, J] could possibly mean:

  • You are implicitly assuming A is a collection of arrays and you want to perform the same indexing operation over all of them (this would be getindex.(A, (I,), (J,)))
  • You are implicitly assuming A is a single array and you want to broadcast over the passed indices — great for fusing these accesses or doing “diagonal” indexing (this would be getindex.((A,), I, J))
  • Or you (probably don’t) want both the array and the indices to participate in the broadcast simultaneously (this would be getindex.(A, I, J))
5 Likes

I get it, but like… does anyone look at that and not expect the second one? I understand you could interpret other ways, but assuming A is an array of arrays in the first and last case seems IMO contrarian and not really the way most people would read that syntax. Maybe someone will say differently, but I haven’t seen someone outside of that thread assume it would or could mean anything but getindex.((A,), I, J) like OP says. This is really a case of “perfect is the enemy of good” in my view.

3 Likes

If you apply the rule we apply for every other function it would be number 3 though.

2 Likes

Yes, both me and OP. I often miss a generalization of first, last that work for any index, and have often wanted exactly what’s requested in OP.

Edit: after having read your post again, I’m not quite sure which one you’re arguing for. In any case, I tend to miss the same functionality as OP.

7 Likes

should be this one right? then the others can be disambiguated like Ref(A).[I, J], A.[Ref(I), Ref(J)], etc.

3 Likes

That looks reasonable enough.

1 Like

An alternative would be to stack it into a matrix and then just use regular indexing:

u = stack(v)
u[1, :]
u[2, :]
...
1 Like

Wrt developing multi indices access, as in getindex.(Ref(x), is), further: also consider a more principled approach of GitHub - andyferris/Indexing.jl: Generalized indexing for Julia. There Y = getindices(X, I) always implies that Y has the same indices as I with values of X:

getindices(container, indices)

Return an indexable container with indices `keys(indices)` and values `container[i]` for
`i ∈ values(indices)`. This generalizes scalar `getindex(container, index)` for multiple
indices, for dictionaries, tuples, named tuples, etc.