If you store labels and breakpoints as arrays then you can immediately take advantage of the searchsortedlast()
function.
struct LabelledRanges{T<:Real}
breakpoints::AbstractArray{T}
labels::AbstractArray
isdesc::Bool
end
function LabelledRanges(breakpoints, labels, isdesc=true)
if length(breakpoints) != length(labels)-1
error("There must be exactly one more label than breakpoints.")
elseif !issorted(breakpoints)
error("Breakpoints must be sorted.")
else
LabelledRanges(breakpoints, labels, isdesc)
end
end
getlabel(x::Real, r::LabelledRanges) = r.labels[1+searchsortedlast(r.breakpoints, x, lt=(r.isdesc ? (<=) : (<)))]
Usage:
julia> lr = LabelledRanges([0.0,0.1,0.3,0.5], ["below","A","B","C","above"], true);
julia> lr2 = LabelledRanges([0.0,0.1,0.3,0.5], ["below","A","B","C","above"], false);
julia> xx = [-1.0, 0.0, 0.01, 0.09, 0.1, 0.11, 0.29, 0.30, 0.31, 0.49, 0.50, 0.51];
julia> [x [getlabel(x,lr) for x in xx] [getlabel(x,lr2) for x in xx]]
-1.0 "below" "below"
0.0 "below" "A"
0.01 "A" "A"
0.09 "A" "A"
0.1 "A" "B"
0.11 "B" "B"
0.29 "B" "B"
0.3 "B" "C"
0.31 "C" "C"
0.49 "C" "C"
0.5 "C" "above"
0.51 "above" "above"
You may prefer to have the “direction of evaluation” to be a parameter of the getlabel()
function instead of a property of LabelledRanges
. Both choices are defendable I think.