# Iteration over OffsetArray

From this comment, I learned that `for i = eachindex(v)` is preferred to `for i = 1:length(v)` when iterating over `v::AbstractVector`, because `AbstractVector` may not support 1-based indexing (e.g., `OffsecArrays.jl`).

`eachindex(v)` is acceptable when we want to iterate over the entire length of `v`. However, sometimes we want to iterate over a portion of `v`. For example, to calculate the gap between adjacent entries, we would want to do something like

``````for i = 1:length(v)-1
println(v[i+1] - v[i])
end
``````

In this case we cannot use `eachindex(v)`. Is there a syntax that we can use in such a case?

(Calculating the gap between adjacent entries is what `diff(v)` does, and `diff(v)` calls `Base.require_one_based_indexing(v)`, which suggests that there may not be a simple way of handling this issue.)

``````for i = firstindex(v):lastindex(v)-1
``````
2 Likes

Or

``````for i in eachindex(v)[begin:end-1]
``````

which produces the same result - it’s mostly a matter of style/taste.

And would this be a good replacement:

``````    # only for one based arrays
x=vec
y=vec
# generic
x=vec[firstindex(vec)]]
y=vec[firstindex(vec)+1]
``````

?

this allocates a concrete array, `O(N)` in memory, would not recommend

``````x = vec[begin]
y = vec[begin + 1]
``````

it’s exactly how the `begin/end` syntax sugar works under the hood:

``````julia> Meta.@lower vec[begin + 1]
:(\$(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 ─ %1 = Base.firstindex(vec)
│   %2 = %1 + 1
│   %3 = Base.getindex(vec, %2)
└──      return %3
))))
``````
3 Likes

I don’t believe that’s true.

``````julia> let v = OffsetArray([1,2,3,4,5], -2:2)
eachindex(v)[begin:end-1]
end
-2:1

julia> typeof(ans)
UnitRange{Int64}``````
1 Like

oops, Julia is smarter than I thought, my bad

1 Like

This assumes a step length of 1. For complete generality you need to index into `eachindex(v)`.

I would create `SubArray`s via `@view` or `@views`:

``````using OffsetArrays

v = OffsetArray([1,3,2,4,8,90], -5)
v_1, v_2 = @views v[begin:end-1], v[begin+1:end];
println.(v_2 .- v_1)
``````

If you really wanted to use a for loop

``````for i in eachindex(v_1, v_2)
println(v_2[i] - v_1[i])
end
``````

Technically, the result of `axes(A)` where `typeof(A) <: AbstractArray` must be a `tuple of AbstractUnitRange{<:Integer}` according to the `AbstractArray` interface: Interfaces · The Julia Language. The step should be `1`.

The problem is that we do not have a good way of enforcing this.

TIL. I guess StarWarsArrays should not subtype AbstractArray then.

Often, the simplest solution might be to use `OffsetArrays.no_offset_view`, which will return a 1-based view that will (for mutable arrays) share memory with the parent array. So

``````v1 = OffsetArrays.no_offset_view(v)
for i = 1:length(v1)-1 # works now
println(v1[i+1] - v1[i])
end
``````

This might lead to code that’s simpler to understand.

1 Like

Indexing into a range with another range should produce a range. It’s not too hard to do.

I believe the most idiomatic way is

``````for i = axes(v, 1)[begin:end-1]
println(v[i+1] - v[i])
end
``````
1 Like

No, that doesn’t work. It must be `axes(v, 1)[begin:end-1]`:

``````julia> v = OffsetArray(rand(5), -1:3);

julia> eachindex(v)[1:end-1]
1:2

julia> eachindex(v)[begin:end-1]
-1:2
``````

Oops, yes, typo. Edited, thanks.