Skip to content

Commit 1d3dd85

Browse files
authored
Use ismissing(x) instead of x === missing (#44407)
This is more generic and will allow packages to define custom `missing`-like types allowing to distinguish several kinds of missing values like in e.g. Stata and SAS (see https://github.com/nalimilan/TypedMissings.jl). This should have no performance impact now thanks to #38905.
1 parent 05992e7 commit 1d3dd85

File tree

2 files changed

+43
-17
lines changed

2 files changed

+43
-17
lines changed

base/missing.jl

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ function iterate(itr::SkipMissing, state...)
241241
y = iterate(itr.x, state...)
242242
y === nothing && return nothing
243243
item, state = y
244-
while item === missing
244+
while ismissing(item)
245245
y = iterate(itr.x, state)
246246
y === nothing && return nothing
247247
item, state = y
@@ -251,12 +251,12 @@ end
251251

252252
IndexStyle(::Type{<:SkipMissing{T}}) where {T} = IndexStyle(T)
253253
eachindex(itr::SkipMissing) =
254-
Iterators.filter(i -> @inbounds(itr.x[i]) !== missing, eachindex(itr.x))
254+
Iterators.filter(i -> !ismissing(@inbounds(itr.x[i])), eachindex(itr.x))
255255
keys(itr::SkipMissing) =
256-
Iterators.filter(i -> @inbounds(itr.x[i]) !== missing, keys(itr.x))
256+
Iterators.filter(i -> !ismissing(@inbounds(itr.x[i])), keys(itr.x))
257257
@propagate_inbounds function getindex(itr::SkipMissing, I...)
258258
v = itr.x[I...]
259-
v === missing && throw(MissingException(LazyString("the value at index ", I, " is missing")))
259+
ismissing(v) && throw(MissingException(LazyString("the value at index ", I, " is missing")))
260260
v
261261
end
262262

@@ -280,18 +280,18 @@ function _mapreduce(f, op, ::IndexLinear, itr::SkipMissing{<:AbstractArray})
280280
ilast = last(inds)
281281
for outer i in i:ilast
282282
@inbounds ai = A[i]
283-
ai !== missing && break
283+
!ismissing(ai) && break
284284
end
285-
ai === missing && return mapreduce_empty(f, op, eltype(itr))
285+
ismissing(ai) && return mapreduce_empty(f, op, eltype(itr))
286286
a1::eltype(itr) = ai
287287
i == typemax(typeof(i)) && return mapreduce_first(f, op, a1)
288288
i += 1
289289
ai = missing
290290
for outer i in i:ilast
291291
@inbounds ai = A[i]
292-
ai !== missing && break
292+
!ismissing(ai) && break
293293
end
294-
ai === missing && return mapreduce_first(f, op, a1)
294+
ismissing(ai) && return mapreduce_first(f, op, a1)
295295
# We know A contains at least two non-missing entries: the result cannot be nothing
296296
something(mapreduce_impl(f, op, itr, first(inds), last(inds)))
297297
end
@@ -309,7 +309,7 @@ mapreduce_impl(f, op, A::SkipMissing, ifirst::Integer, ilast::Integer) =
309309
return nothing
310310
elseif ifirst == ilast
311311
@inbounds a1 = A[ifirst]
312-
if a1 === missing
312+
if ismissing(a1)
313313
return nothing
314314
else
315315
return Some(mapreduce_first(f, op, a1))
@@ -320,25 +320,25 @@ mapreduce_impl(f, op, A::SkipMissing, ifirst::Integer, ilast::Integer) =
320320
i = ifirst
321321
for outer i in i:ilast
322322
@inbounds ai = A[i]
323-
ai !== missing && break
323+
!ismissing(ai) && break
324324
end
325-
ai === missing && return nothing
325+
ismissing(ai) && return nothing
326326
a1 = ai::eltype(itr)
327327
i == typemax(typeof(i)) && return Some(mapreduce_first(f, op, a1))
328328
i += 1
329329
ai = missing
330330
for outer i in i:ilast
331331
@inbounds ai = A[i]
332-
ai !== missing && break
332+
!ismissing(ai) && break
333333
end
334-
ai === missing && return Some(mapreduce_first(f, op, a1))
334+
ismissing(ai) && return Some(mapreduce_first(f, op, a1))
335335
a2 = ai::eltype(itr)
336336
i == typemax(typeof(i)) && return Some(op(f(a1), f(a2)))
337337
i += 1
338338
v = op(f(a1), f(a2))
339339
@simd for i = i:ilast
340340
@inbounds ai = A[i]
341-
if ai !== missing
341+
if !ismissing(ai)
342342
v = op(v, f(ai))
343343
end
344344
end
@@ -384,7 +384,7 @@ julia> filter(isodd, skipmissing(x))
384384
function filter(f, itr::SkipMissing{<:AbstractArray})
385385
y = similar(itr.x, eltype(itr), 0)
386386
for xi in itr.x
387-
if xi !== missing && f(xi)
387+
if !ismissing(xi) && f(xi)
388388
push!(y, xi)
389389
end
390390
end
@@ -450,7 +450,7 @@ ERROR: `b` is still missing
450450
macro coalesce(args...)
451451
expr = :(missing)
452452
for arg in reverse(args)
453-
expr = :((val = $arg) !== missing ? val : $expr)
453+
expr = :(!ismissing((val = $(esc(arg));)) ? val : $expr)
454454
end
455-
return esc(:(let val; $expr; end))
455+
return :(let val; $expr; end)
456456
end

test/missing.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,3 +651,29 @@ for func in (round, ceil, floor, trunc)
651651
@test Core.Compiler.is_foldable(Base.infer_effects(func, (Type{Int},Union{Int,Missing})))
652652
end
653653
end
654+
655+
@testset "Custom Missing type" begin
656+
struct NewMissing end
657+
Base.ismissing(::NewMissing) = true
658+
Base.coalesce(x::NewMissing, y...) = coalesce(y...)
659+
Base.isless(::NewMissing, ::NewMissing) = false
660+
Base.isless(::NewMissing, ::Any) = false
661+
Base.isless(::Any, ::NewMissing) = true
662+
Base.isequal(::NewMissing, ::Missing) = true
663+
Base.isequal(::Missing, ::NewMissing) = true
664+
arr = [missing 1 2 3 missing 10 11 12 missing]
665+
newarr = Union{Int, NewMissing}[ismissing(v) ? NewMissing() : v for v in arr]
666+
667+
@test all(skipmissing(arr) .== skipmissing(newarr))
668+
@test all(eachindex(skipmissing(arr)) .== eachindex(skipmissing(newarr)))
669+
@test all(keys(skipmissing(arr)) .== keys(skipmissing(newarr)))
670+
@test_broken sum(skipmissing(arr)) == sum(skipmissing(newarr))
671+
@test filter(>(10), skipmissing(arr)) == filter(>(10), skipmissing(newarr))
672+
@test isequal(sort(vec(arr)), sort(vec(newarr)))
673+
674+
@test_throws MissingException skipmissing(newarr)[findfirst(ismissing, newarr)]
675+
@test coalesce(NewMissing(), 1) == coalesce(NewMissing(), NewMissing(), 1) == 1
676+
@test coalesce(NewMissing()) === coalesce(NewMissing(), NewMissing()) === missing
677+
@test @coalesce(NewMissing(), 1) == @coalesce(NewMissing(), NewMissing(), 1) == 1
678+
@test @coalesce(NewMissing()) === @coalesce(NewMissing(), NewMissing()) === missing
679+
end

0 commit comments

Comments
 (0)