Skip to content

Commit

Permalink
Implement hasproperty (#1852)
Browse files Browse the repository at this point in the history
This is consistent with our definition of getproperty and setproperty!.
It also provides an efficient deprecation for haskey, though it turns out
the current deprecation didn't support non-Symbols, contrary to the function
it replaces.
  • Loading branch information
nalimilan authored Jun 18, 2019
1 parent 25e23b7 commit b60abe9
Show file tree
Hide file tree
Showing 10 changed files with 42 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ julia = "1"
Missings = ">= 0.2.3"
CategoricalArrays = ">= 0.5.4"
StatsBase = ">= 0.11.0"
Compat = ">= 0.59.0"
Compat = "2.0.0"
Tables = ">= 0.2.3"
IteratorInterfaceExtensions = "0.1.1, 1"
TableTraits = "0.4, 1"
Expand Down
5 changes: 4 additions & 1 deletion src/DataFrames.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export AbstractDataFrame,
rename,
select,
select!,
showcols,
stack,
stackdf,
unique!,
Expand All @@ -67,6 +66,10 @@ else
export eachcol, eachrow
end

if VERSION < v"1.2"
export hasproperty
end

##############################################################################
##
## Load files
Expand Down
4 changes: 3 additions & 1 deletion src/abstractdataframe/abstractdataframe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ abstract type AbstractDataFrame end
Base.names(df::AbstractDataFrame) = names(index(df))
_names(df::AbstractDataFrame) = _names(index(df))

Compat.hasproperty(df::AbstractDataFrame, s::Symbol) = haskey(index(df), s)

"""
Set column names
Expand Down Expand Up @@ -1137,7 +1139,7 @@ function _vcat(dfs::AbstractVector{<:AbstractDataFrame};
all_cols = Vector{AbstractVector}(undef, length(header))
for (i, name) in enumerate(header)
newcols = map(dfs) do df
if haskey(index(df), name)
if hasproperty(df, name)
return df[name]
else
Iterators.repeated(missing, nrow(df))
Expand Down
6 changes: 3 additions & 3 deletions src/abstractdataframe/join.jl
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ function Base.join(df1::AbstractDataFrame,
if indicator !== nothing
indicator_cols = ["_left", "_right"]
for i in 1:2
while (haskey(index(df1), Symbol(indicator_cols[i])) ||
haskey(index(df2), Symbol(indicator_cols[i])) ||
while (hasproperty(df1, Symbol(indicator_cols[i])) ||
hasproperty(df2, Symbol(indicator_cols[i])) ||
Symbol(indicator_cols[i]) == indicator)
indicator_cols[i] *= 'X'
end
Expand Down Expand Up @@ -400,7 +400,7 @@ function Base.join(df1::AbstractDataFrame,
indicatorcol = CategoricalArray{String,1}(refs, CategoricalPool{String,UInt8}(["left_only", "right_only", "both"]))
unique_indicator = indicator
try_idx = 0
while haskey(index(joined), unique_indicator)
while hasproperty(joined, unique_indicator)
try_idx += 1
unique_indicator = Symbol(string(indicator, "_", try_idx))
end
Expand Down
10 changes: 5 additions & 5 deletions src/dataframe/dataframe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -395,11 +395,11 @@ end

function nextcolname(df::DataFrame)
col = Symbol(string("x", ncol(df) + 1))
haskey(index(df), col) || return col
hasproperty(df, col) || return col
i = 1
while true
col = Symbol(string("x", ncol(df) + 1, "_", i))
haskey(index(df), col) || return col
hasproperty(df, col) || return col
i += 1
end
end
Expand All @@ -417,7 +417,7 @@ function insert_single_column!(df::DataFrame,
j = index(df)[col_ind]
_columns(df)[j] = dv
else
if typeof(col_ind) <: Symbol
if col_ind isa Symbol
push!(index(df), col_ind)
push!(_columns(df), dv)
else
Expand Down Expand Up @@ -778,14 +778,14 @@ function insertcols!(df::DataFrame, col_ind::Int, name_col::Pair{Symbol, <:Abstr
0 < col_ind <= ncol(df) + 1 || throw(BoundsError())
size(df, 1) == length(item) || size(df, 2) == 0 || error("number of rows does not match")

if haskey(index(df), name)
if hasproperty(df, name)
if makeunique
k = 1
while true
# we only make sure that new column name is unique
# if df originally had duplicates in names we do not fix it
nn = Symbol("$(name)_$k")
if !haskey(index(df), nn)
if !hasproperty(df, nn)
name = nn
break
end
Expand Down
2 changes: 1 addition & 1 deletion src/dataframerow/dataframerow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Base.haskey(r::DataFrameRow, key::Bool) =
throw(ArgumentError("invalid key: $key of type Bool"))
Base.haskey(r::DataFrameRow, key::Integer) = 1 key size(r, 1)
function Base.haskey(r::DataFrameRow, key::Symbol)
haskey(parent(r), key) || return false
hasproperty(parent(r), key) || return false
index(r) isa Index && return true
# here index(r) is a SubIndex
pos = index(parent(r))[key]
Expand Down
6 changes: 4 additions & 2 deletions src/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1399,7 +1399,9 @@ import Base: get
@deprecate get(df::AbstractDataFrame, key::Any, default::Any) key in names(df) ? df[key] : default

import Base: haskey
@deprecate haskey(df::AbstractDataFrame, key::Any) key in names(df)
@deprecate haskey(df::AbstractDataFrame, key::Symbol) hasproperty(df, key)
@deprecate haskey(df::AbstractDataFrame, key::Integer) key in 1:ncol(df)
@deprecate haskey(df::AbstractDataFrame, key::Any) key in 1:ncol(df) || key in names(df)

import Base: empty!
@deprecate empty!(df::DataFrame) deletecols!(df, 1:ncol(df))
@deprecate empty!(df::DataFrame) deletecols!(df, 1:ncol(df))
2 changes: 1 addition & 1 deletion src/other/broadcasting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function Base.maybeview(df::AbstractDataFrame, idxs)
throw(ArgumentError("Broadcasting into a data frame with no columns is not allowed"))
end
if idxs isa Symbol
if !haskey(index(df), idxs)
if !hasproperty(df, idxs)
if !(df isa DataFrame)
# this will throw an appropriate error message
df[idxs]
Expand Down
9 changes: 9 additions & 0 deletions test/dataframe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ end
@test size(similar(df, 2)) == size(missingdf)
end

@testset "hasproperty" begin
df = DataFrame(a=[1, 2])
@test hasproperty(df, :a)
@test !hasproperty(df, :c)
@test_throws MethodError hasproperty(df, 1)
@test_throws MethodError hasproperty(df, 1.5)
@test_throws MethodError hasproperty(df, true)
end

@testset "insertcols!" begin
df = DataFrame(a=Union{Int, Missing}[1, 2], b=Union{Float64, Missing}[3.0, 4.0])
@test_throws BoundsError insertcols!(df, 5, :newcol => ["a", "b"], )
Expand Down
11 changes: 11 additions & 0 deletions test/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,15 @@ end
@test isempty(DataFrame(a=[], b=[]))
end

@testset "haskey" begin
df = DataFrame(x=1:3)
@test haskey(df, 1)
@test !haskey(DataFrame(), 1)
@test !haskey(df, 2)
@test !haskey(df, 0)
@test haskey(df, :x)
@test !haskey(df, :a)
@test !haskey(df, "a")
end

end # module

0 comments on commit b60abe9

Please sign in to comment.