diff --git a/Project.toml b/Project.toml index 2b7c57a..a6bc0a3 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "DataAPI" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" authors = ["quinnj "] -version = "1.0.1" +version = "1.1.0" [compat] julia = "1" diff --git a/src/DataAPI.jl b/src/DataAPI.jl index 08360ab..14b4f1d 100644 --- a/src/DataAPI.jl +++ b/src/DataAPI.jl @@ -73,6 +73,26 @@ definition. """ function describe end +""" + levels(x) + +Return a vector of unique values which occur or could occur in collection `x`, +omitting `missing` even if present. Values are returned in the preferred order +for the collection, with the result of [`sort`](@ref) as a default. + +Contrary to [`unique`](@ref), this function may return values which do not +actually occur in the data, and does not preserve their order of appearance in `x`. +""" +function levels(x) + T = Base.nonmissingtype(eltype(x)) + levs = convert(AbstractArray{T}, filter!(!ismissing, unique(x))) + try + sort!(levs) + catch + end + levs +end + """ Between(first, last) diff --git a/test/runtests.jl b/test/runtests.jl index a6b32c7..a870f42 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -27,6 +27,36 @@ end end +@testset "levels" begin + + @test DataAPI.levels(1:1) == + DataAPI.levels([1]) == + DataAPI.levels([1, missing]) == + DataAPI.levels([missing, 1]) == + [1] + @test DataAPI.levels(2:-1:1) == + DataAPI.levels([2, 1]) == + DataAPI.levels(Any[2, 1]) == + DataAPI.levels([2, missing, 1]) == + [1, 2] + @test DataAPI.levels([missing, "a", "c", missing, "b"]) == ["a", "b", "c"] + @test DataAPI.levels([Complex(0, 1), Complex(1, 0), missing]) == + [Complex(0, 1), Complex(1, 0)] + @test typeof(DataAPI.levels([1])) === + typeof(DataAPI.levels([1, missing])) === + Vector{Int} + @test typeof(DataAPI.levels(["a"])) === + typeof(DataAPI.levels(["a", missing])) === + Vector{String} + @test typeof(DataAPI.levels(Real[1])) === + typeof(DataAPI.levels(Union{Real,Missing}[1, missing])) === + Vector{Real} + @test typeof(DataAPI.levels(trues(1))) === Vector{Bool} + @test isempty(DataAPI.levels([missing])) + @test isempty(DataAPI.levels([])) + +end + @testset "Between" begin for x in (1, :a), y in (1, :a)