I often need a range that doesn’t include the last point:
function func(x1, x2, del)
for x in x1:del:x2
# but I want the loop only for x != x2
Depending on the values of x1
, x2
, and del
, the loop sometimes includes x == x2
.
Is there already a library function or a user-defined type to handle this case? Needless to say, you can always terminate the loop by
for x in x1:del:x2
(x == x2) && break
. . .
Also, if you go fancy, you can define your own type with your own iterator to achieve this.
But, I just want to know what I’m missing, before writing inelegant code like the above example or going the fancy way of wring a custom type.
1 Like
Maybe
for x in x1:del:x2-del
...
Why not just (x1:del:x2)[1:end-1]
?
julia> x1 = 3; del = 2; x2 = 19;
julia> x1:del:x2
3:2:19
julia> (x1:del:x2)[1:end-1]
3:2:17
for x in x1:del:x2-del
In the initial post, I should have written that that’s not a solution:
function func(x1, del, x2)
display(collect(x1:del:x2))
display(collect(x1:del:(x2-del)))
end
x1 = 0.0
x2 = 2.5
delx = 1.0
func(x1, del, x2)
In this case, x1:del:x2
is what I want, because all the values satisfy x < x2
.
Unless you know the values of x1
, x2
, and del
beforehand, it’s impossible to determine whether x2
or x2 - del
is the right end of the UnitRange I want.
Why not just (x1:del:x2)[1:end-1]
?
Because you can’t tell whether (x1:del:x2)[1:end]
or (x1:del:x2)[1:end-1]
is the correct answer:
0.0:1.0:2.5 # -> [0.0, 1.0, 2.0] < 2.5 . . . I want this!
0.0:1.0:2.0 # -> [0.0, 1.0, 2.0] . . . I don't want this because 2.0 == x2
Only when you know the values of x1
, del
, and x2
can you tell whether x1:del:x2
is the right one or not.
I’m not making this up. My real need is to generate a range of DataTime
that doesn’t include the end point:
using Dates
t1 = DateTime(1988,1,1)
t2 = DateTime(2023,12,31)
delt = Dates.Day(3)
t1:delt:t2 # Does this sequence include t2 ?
You can’t answer the question unless you actually calculate the range t2 - t1
in the units of days and divide it by 3.
It’s inelegant that you have to do such a calculation each time you handle different values that constitute the range.
1 Like
Ahh, felt like it was too simple. Maybe you can just remove a machine epsilon for the type? Feels hacky but works for your example.
julia> x = 0
0
julia> y1 = 2.5
2.5
julia> y2 = 2.0
2.0
julia> del = 1.0
1.0
julia> x:del:y1-eps(y1)
0.0:1.0:2.0
julia> x:del:y2-eps(y2)
0.0:1.0:1.0
this package seems like the solution for the problem: GitHub - jw3126/RangeHelpers.jl: Make ranges not bugs , but it does not support Date ranges. (PR incoming)
3 Likes
in this particular case:
using RangeHelpers, Dates
t1 = DateTime(1988,1,1)
t2 = DateTime(2023,12,31)
delt = Dates.Day(3)
r = RangeHelpers.range(start = t1, step = delt, stop = strictbelow(t2))
that gives:
julia> RangeHelpers.range(start = t1, stop = strictbelow(t2), step = delt)
DateTime("1988-01-01T00:00:00"):Day(3):DateTime("2023-12-29T00:00:00")
julia> RangeHelpers.range(start = 0.0, stop = strictbelow(2.0),step = 1.0)
0.0:1.0:1.0
julia> RangeHelpers.range(start = 0.0, stop = strictbelow(2.5),step = 1.0)
0.0:1.0:2.0
4 Likes
HanD
July 31, 2023, 7:49am
9
It sure sounds like that there should be a package for that out there somewhere…
How about this?
Iterators.takewhile(<(x2), x1:del:x2)
3 Likes
DNF
July 31, 2023, 7:55am
10
ryofurue:
x1:del:x2
Just for the sake of clearer communication, the above is not a UnitRange
, but a StepRangeLen
. In fact, even
julia> (1.0:3.0) isa UnitRange
false
It’s better to just call it a ‘range’, maybe you should rename the thread to “Open-ended range?”
Maybe you can try something like (for float inputs only)
openrange(start, stop, step) = range(start, prevfloat(stop); step)
(I also like the takewhile
-solution of @HanD , though it seems to be slower, probably due to repeated comparisons).
2 Likes
Just for the sake of clearer communication, the above is not a UnitRange
, but a StepRangeLen
.
[ . . . ]
It’s better to just call it a ‘range’, maybe you should rename the thread to “Open-ended range?”
Thank you for your kind correction! Yes, I’ve edited my initial post. [Edit: I’ve also edited the other one which also included the wrong terminology.]
julia> function openrigthrange(sta, ste, sto)
len=Int((sto-sta) ÷ ste)+1
sto1= sta+(len-1)*ste
len-=(sto1 >=sto)
range(start=sta,length=len,step=ste)
end
openrigthrange (generic function with 1 method)
julia> openrigthrange(0.,1.1,3.0)
0.0:1.1:2.2
julia> openrigthrange(0.,1.1,3.3)
0.0:1.1:2.2
I may have misunderstood you . . . Your example doesn’t compile. Did you mean that in the future the RangeHelpers
package will work as you describe?
Here is the code
longemen3000:
using RangeHelpers, Dates
t1 = DateTime(1988,1,1)
t2 = DateTime(2023,12,31)
delt = Dates.Day(3)
r = RangeHelpers.range(start = t1, step = delt, stop = strictbelow(t2))
which doesn’t compile. See the error listing at the end of this message. The range
function seems to be trying to compare an Int
with a Day
. I’m using RangeHelpers v0.1.9
.
ERROR: LoadError: MethodError: no method matching isless(::Int64, ::Day)
Closest candidates are:
isless(::Union{Month, Quarter, Year}, ::Union{Day, Hour, Microsecond, Millisecond, Minute, Nanosecond, Second, Week})
@ Dates ~/.julia/juliaup/julia-1.9.2+0.aarch64.apple.darwin14/share/julia/stdlib/v1.9/Dates/src/periods.jl:439
isless(::P, ::P) where P<:Period
@ Dates ~/.julia/juliaup/julia-1.9.2+0.aarch64.apple.darwin14/share/julia/stdlib/v1.9/Dates/src/periods.jl:71
isless(::Period, ::Period)
@ Dates ~/.julia/juliaup/julia-1.9.2+0.aarch64.apple.darwin14/share/julia/stdlib/v1.9/Dates/src/periods.jl:97
...
Stacktrace:
[1] <(x::Int64, y::Day)
@ Base ./operators.jl:343
[2] <=(x::Int64, y::Day)
@ Base ./operators.jl:392
[3] >=(x::Day, y::Int64)
@ Base ./operators.jl:416
[4] start_step_stop(start::DateTime, step::Day, stop::RangeHelpers.Approach{DateTime})
@ RangeHelpers ~/.julia/packages/RangeHelpers/crKui/src/RangeHelpers.jl:246
[5] range1(start::DateTime, step::Day, stop::RangeHelpers.Approach{DateTime}, length::Nothing)
@ RangeHelpers ~/.julia/packages/RangeHelpers/crKui/src/RangeHelpers.jl:231
[6] range(start::DateTime; stop::RangeHelpers.Approach{DateTime}, length::Nothing, step::Day)
@ RangeHelpers ~/.julia/packages/RangeHelpers/crKui/src/RangeHelpers.jl:141
[7] range0(start::DateTime, step::Day, stop::RangeHelpers.Approach{DateTime}, length::Nothing)
@ RangeHelpers ~/.julia/packages/RangeHelpers/crKui/src/RangeHelpers.jl:154
[8] range(; start::DateTime, stop::RangeHelpers.Approach{DateTime}, length::Nothing, step::Day)
@ RangeHelpers ~/.julia/packages/RangeHelpers/crKui/src/RangeHelpers.jl:137
[9] top-level scope
@ ~/Dropbox/tmp/try-openrange.jl:5
in expression starting at /Users/furue/Dropbox/tmp/try-openrange.jl:5
1 Like
yes, the example does not compile, because of a bug in the library, that i’m fixing at the moment (0 -> `zero(step)` by longemen3000 · Pull Request #3 · jw3126/RangeHelpers.jl · GitHub )
1 Like