Convert axes(a) etc to StepRange with a step other than 1

I want to loop over a matrix that might have custom indices. I can use axes(a) to get these. But I need a step size other than 1.

Is there a simple, generic way of converting Base.OneTo to a StepRange with a step other than 1 besides just doing it manually?

It would be good to have a helper method for this, I can’t imagine why else you would need to convert anything to a step range except to have a step size other than 1!

Something like this could convert axes(a) to StepRanges with a step size 2:

StepRange.(axes(a), 2)

@Per has the better solution.

alternatively:

[first(x):3:last(x) for x in  axes(a)]
2 Likes

It’s somehow unsatisfying to have to use a list comprehension for the most common reason for this conversion…

It’s possible to use a dot-broadcast instead of a list comprehension.

(x -> first(x):3:last(x)).(axes(a))

This is likely more efficient, as it results in a tuple instead of an array.

1 Like
julia> StepRange.(first.(axes(a)),3,last.(axes(a)))
(1:3:7, 1:3:10, 1:3:13)

julia> function steprange(a::AbstractArray, stepby)
           stops = axes(a)
           return StepRange.(first.(stops), stepby, last.(stops))
       end
steprange (generic function with 1 method)

julia> steprange(a, 3)
(1:3:7, 1:3:10, 1:3:13)
# ----

julia> a = fill(0, (8+1,12+1,16+1));axes(a)
(Base.OneTo(9), Base.OneTo(13), Base.OneTo(17))

julia> steps = (2,3,4)
(2, 3, 4)

julia> steprange(a, steps)
(1:2:9, 1:3:13, 1:4:17)

julia> collect.(ans)
([1, 3, 5, 7, 9], [1, 4, 7, 10, 13], [1, 5, 9, 13, 17])
julia> Base.StepRange(first::T, step::T, last::AbstractRange) where {T<:Integer} =
           StepRange(first, step, last.stop)

julia> StepRange(2, 5, Base.OneTo(17))
2:5:17

julia> collect(ans)
4-element Array{Int64,1}:
  2
  7
 12
 17
1 Like

Yeah these are better! I imagine a lot faster as well without the allocation.

It would still be nice to have a method that accepted any other range and a step size…

Lets get that in base now :wink:

Edit: well a StepRange(x, step) constructor that accepted OneTo and the stepsize, anyway

I’d rather have a functon restep(x, step) = first(x):step:last(x) that would work on any range, not just Base.OneTo.

But with really small, rarely used functions like this, it is sometimes better to write out the code explicitly each time, rather than memorizing a function name and what it does.

Totally, that’s what I meant. But you would probably have to write a few specific implementations for OneTo and Range etc unless they just convert(). Edit: no you probably wouldn’t…

I also agree in principle that keeping rare function overhead down is a good idea. But in this case there are very few reasons to ever manually use StepRange(x) and at least a few of reasons to use StepRange(x, step), so I’m not sure that really applies.

Yeah, this is what I love about Julia.

1 Like

Hah yeah I’m still not used to it! So much useless boilerplate to unlearn.