-
Notifications
You must be signed in to change notification settings - Fork 33
/
skipmissing.jl
55 lines (49 loc) · 1.75 KB
/
skipmissing.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
"""
skipmissing(itr::Raster)
Returns an iterable over the elements in a `Raster` object, skipping any values equal to either the `missingval` or `missing`.
"""
function Base.skipmissing(itr::Raster)
if ismissing(missingval(itr))
Base.SkipMissing(itr)
else
SkipMissingVal(itr)
end
end
struct SkipMissingVal{T}
x::T
end
Base.IteratorSize(::Type{<:SkipMissingVal}) = Base.SizeUnknown()
Base.IteratorEltype(::Type{SkipMissingVal{T}}) where {T} = Base.IteratorEltype(T)
Base.eltype(::Type{SkipMissingVal{T}}) where {T} = Base.nonmissingtype(eltype(T))
missingval(itr::SkipMissingVal) = missingval(itr.x)
function Base.iterate(itr::SkipMissingVal, state...)
y = iterate(itr.x, state...)
y === nothing && return nothing
item, state = y
# We check for both `missing` and the raster `missingval`
# Mostly the compiler should elide the `missing` check?
while _missing(item, itr)
y = iterate(itr.x, state)
y === nothing && return nothing
item, state = y
end
item, state
end
_missing(x, itr) = isequal(x, missingval(itr))
_missing(x::Missing, itr) = true
_missing(x::Nothing, itr) = false
Base.IndexStyle(::Type{<:SkipMissingVal{T}}) where {T} = IndexStyle(T)
Base.eachindex(itr::SkipMissingVal) =
Iterators.filter(i -> !_missing(@inbounds(itr.x[i]), itr), eachindex(itr.x))
Base.keys(itr::SkipMissingVal) =
Iterators.filter(i -> !_missing(@inbounds(itr.x[i]), itr), keys(itr.x))
@propagate_inbounds function Base.getindex(itr::SkipMissingVal, I...)
v = itr.x[I...]
_missing(v, itr) && throw(MissingException("the value at index $I is the raster missingval"))
v
end
function Base.show(io::IO, s::SkipMissingVal)
print(io, "skipmissing(")
show(io, s.x)
print(io, ')')
end