# First class "from end" indices into array

Is there any way to make a first class index that points to an element from the end of an array? So it can be e.g. assigned to a variable. For example, in python I can do

``````idx1 = 0
idx2 = -1
a = [1,2,3,4,5]
a[idx1] # Gives 1
a[idx2] # Gives 5
``````

In Julia one can use a special syntax with `end` keyword to access arrays from the end, but `end` has its special meaning only inside square brackets. Is there some way, e.g. a special type `FromEnd`, so I can write

``````idx = FromEnd(1)
a = [1,2,3,4,5]
a[idx] # Gives 5

indices = CartesianIndex(1):CartesianIndex(FromEnd(1)) # Gives all indices but the last
``````
``````julia> a = [1,2,3,4,5]
5-element Vector{Int64}:
1
2
3
4
5

julia> a[end]
5

julia> a[end-(2-1)]
4

julia> a[end-(3-1)]
3

julia> fromend(arr,n)=arr[end-(n-1)]
fromend (generic function with 1 method)

julia> fromend(a,4)
2
``````
1 Like

This is not what I want. In Python you do not have to know the array to create an index into that array. Please notice a difference between

``````a[-1]
``````

and

``````a[fromend(a,1)]
``````

In the first case `a` appears only once.

You can use the `to_indices` mechanism to do this:

``````julia> struct FromEnd
i::Int
end

julia> Base.to_indices(A, inds, I::Tuple{FromEnd, Vararg}) = (@inline; (last(inds[1])-I[1].i, to_indices(A, Base.safe_tail(inds), Base.tail(I))...))

julia> I = FromEnd(2)
FromEnd(2)

julia> arr = reshape(1:15, 5, 3)
5Ă—3 reshape(::UnitRange{Int64}, 5, 3) with eltype Int64:
1   6  11
2   7  12
3   8  13
4   9  14
5  10  15

julia> arr[:,I]
5-element Vector{Int64}:
1
2
3
4
5

julia> arr[I,:]
3-element Vector{Int64}:
3
8
13

``````

Edit: This is not a full solution, just a proof-of-concept.

2 Likes

There is no firstclass negative index in Julia.
The best you can do is
a[end-(1-1)] for a[-1]
a[end-(2-1)] for a[-2]
a[end-(3-1)] for a[-3]

Cool, thanks. But since UnitRange has kind * â†’ * (i.e. it has only one parameter), itâ€™s fundamentally impossible to write something like x:FromEnd(y), isnâ€™t it?

Well for this to work youâ€™d need to hook into `UnitRange` and the like which likely will be very messy and doesnâ€™t sound like a great idea to me. The problem is that a `UnitRange` is concept separate from indexing that doesnâ€™t really make sense without a well-defined endpoint.

Edit: Ofc itâ€™s not â€śfundamentally impossibleâ€ť to do what you ask - the question is rather â€śHow much effort do you need?â€ť. For the mixed ranges to work, you probably need another type.

OK, thanks! Also, ranges like that are not supported in python either:

``````>>> 0:-1
File "<stdin>", line 1
0:-1
^
SyntaxError: illegal target for annotation
``````

Are you OK with not using the `getindex` / `[ ]` syntax directly?

If yes, what about a variation of this (with a better name)?

``````function getindex_potentially_from_end(a, i)
if i > 0
return a[i]
else
return a[end + i]
end
end
``````

Of course this particular example probably only works with standard 1-based vectors, but it just requires one function and no extra types. Just as an idea.

It can be a solution, but it breaks Julia syntax for indexing I like @abraemer 's solution more

1 Like

EndpointRanges.jl provides something like this:

``````julia> a = [1,2,3,4,5]
5-element Vector{Int64}:
1
2
3
4
5

julia> a[iend]
5

julia> idx2 = iend
EndpointRanges.IEnd()

julia> a[idx2]
5
``````
5 Likes

This is great!

When used as indices, the keywords `begin` and `end` desugar to `firstindex` and `lastindex` respectively.

We can confirm that ourselves with `Meta.@lower`:

``````julia> Meta.@lower begin
vec[end]
end
:(\$(Expr(:thunk, CodeInfo(
@ REPL[7]:2 within `top-level scope`
1 â”€ %1 = vec
â”‚   %2 = Base.lastindex(vec)
â”‚   %3 = Base.getindex(%1, %2)
â””â”€â”€      return %3
))))
``````