< Summary

Class:src/index.jl
Assembly:Default
File(s):src/index.jl
Covered lines:19
Uncovered lines:57
Coverable lines:76
Total lines:135
Line coverage:25% (19 of 76)
Covered branches:0
Total branches:0
Tag:43_456648716

File(s)

src/index.jl

#LineLine coverage
 1# simplified form of https://github.com/JuliaData/DataFrames.jl/blob/master/src/other/index.jl
 2abstract type AbstractIndex end
 3
 4struct Index <: AbstractIndex   # an OrderedDict would be nice here...
 165    lookup::Dict{AbstractString, Int}      # name => names array position
 6    names::Vector{AbstractString}
 7end
 8
 9function Index(names::Array{T, 1}) where T <: AbstractString
 810    @assert allunique(names) "names must be unique check for $names"
 811    lookup = Dict{AbstractString, Int}(zip(names, 1:length(names)))
 812    Index(lookup, names)
 13end
 14
 015Index() = Index(Dict{AbstractString, Int}(), String[])
 016Base.length(x::Index) = length(x.names)
 617Base.names(x::Index) = copy(x.names)
 018_names(x::Index) = x.names
 019Base.copy(x::Index) = Index(copy(x.lookup), copy(x.names))
 020Base.isequal(x::AbstractIndex, y::AbstractIndex) = _names(x) == _names(y) # it is enough to check names
 021Base.:(==)(x::AbstractIndex, y::AbstractIndex) = isequal(x, y)
 22
 423Base.haskey(x::Index, key::AbstractString) = haskey(x.lookup, key)
 024Base.haskey(x::Index, key::Integer) = 1 <= key <= length(x.names)
 025Base.haskey(x::Index, key::Bool) =
 26    throw(ArgumentError("invalid key: $key of type Bool"))
 027Base.keys(x::Index) = names(x)
 28
 029@inline Base.getindex(x::AbstractIndex, idx::Bool) = throw(ArgumentError("invalid index: $idx of type Bool"))
 30
 031@inline function Base.getindex(x::AbstractIndex, idx::Integer)
 032    if !(1 <= idx <= length(x))
 033        throw(BoundsError("attempt to access a Index with $(length(x)) columns at index $idx"))
 34    end
 035    Int(idx)
 36end
 37
 038@inline function Base.getindex(x::AbstractIndex, idx::AbstractVector{Int})
 039    isempty(idx) && return idx
 040    minidx, maxidx = extrema(idx)
 041    if minidx < 1
 042        throw(BoundsError("attempt to access a Index with $(length(x)) columns at index $minidx"))
 43    end
 044    if maxidx > length(x)
 045        throw(BoundsError("attempt to access a Index with $(length(x)) columns at index $maxidx"))
 46    end
 047    allunique(idx) || throw(ArgumentError("Elements of $idx must be unique"))
 048    idx
 49end
 50
 051@inline function Base.getindex(x::AbstractIndex, idx::AbstractRange{Int})
 052    isempty(idx) && return idx
 053    minidx, maxidx = extrema(idx)
 054    if minidx < 1
 055        throw(BoundsError("attempt to access a Index with $(length(x)) columns at index $minidx"))
 56    end
 057    if maxidx > length(x)
 058        throw(BoundsError("attempt to access a Index with $(length(x)) columns at index $maxidx"))
 59    end
 060    allunique(idx) || throw(ArgumentError("Elements of $idx must be unique"))
 061    idx
 62end
 63
 064@inline Base.getindex(x::AbstractIndex, idx::AbstractRange{<:Integer}) = getindex(x, collect(Int, idx))
 065@inline Base.getindex(x::AbstractIndex, ::Colon) = Base.OneTo(length(x))
 66
 067@inline function Base.getindex(x::AbstractIndex, idx::AbstractVector{<:Integer})
 068    if any(v -> v isa Bool, idx)
 069        throw(ArgumentError("Bool values except for AbstractVector{Bool} are not allowed for column indexing"))
 70    end
 071    getindex(x, Vector{Int}(idx))
 72end
 73
 074@inline Base.getindex(x::AbstractIndex, idx::AbstractRange{Bool}) = getindex(x, collect(idx))
 75
 076@inline function Base.getindex(x::AbstractIndex, idx::AbstractVector{Bool})
 077    length(x) == length(idx) || throw(BoundsError(x, idx))
 078    findall(idx)
 79end
 80
 81# catch all method handling cases when type of idx is not narrowest possible, Any in particular
 082@inline function Base.getindex(x::AbstractIndex, idxs::AbstractVector)
 083    isempty(idxs) && return Int[] # special case of empty idxs
 084    if idxs[1] isa Real
 085        if !all(v -> v isa Integer && !(v isa Bool), idxs)
 086            throw(ArgumentError("Only Integer values allowed when indexing by vector of numbers"))
 87        end
 088        return getindex(x, convert(Vector{Int}, idxs))
 89    end
 090    idxs[1] isa AbstractString && return getindex(x, convert(Vector{AbstractString}, idxs))
 091    throw(ArgumentError("idxs[1] has type $(typeof(idxs[1])); "*
 92                        "Only Integer or String values allowed when indexing by vector"))
 93end
 94
 095@inline function Base.getindex(x::AbstractIndex, rx::Regex)
 096    getindex(x, filter(name -> occursin(rx, String(name)), _names(x)))
 97end
 98
 99"""
 100    fuzzymatch(l::Dict, idx::AbstractString)
 101# Fuzzy matching rules:
 102# 1. ignore case
 103# 2. maximum Levenshtein distance is 2
 104# 3. always show matches with 0 difference (wrong case)
 105# 4. on top of 3. do not show more than 8 matches in total
 106# Returns candidates ordered by (distance, name) pair
 107"""
 108function fuzzymatch(l::Dict{AbstractString, Int}, idx::AbstractString)
 1109        idxs = uppercase(idx)
 1110        dist = [(REPL.levenshtein(uppercase(x), idxs), x) for x in keys(l)]
 1111        sort!(dist)
 1112        c = [count(x -> x[1] <= i, dist) for i in 0:2]
 1113        maxd = max(0, searchsortedlast(c, 8) - 1)
 1114        [s for (d, s) in dist if d <= maxd]
 115end
 116
 117@inline function lookupname(l::Dict{AbstractString, Int}, idx::AbstractString)
 49118    i = get(l, idx, nothing)
 25119    if i === nothing
 1120        candidates = fuzzymatch(l, idx)
 1121        if isempty(candidates)
 1122            throw(ArgumentError("column name :$idx not found in the data frame"))
 123        end
 0124        candidatesstr = join(string.(':', candidates), ", ", " and ")
 0125        throw(ArgumentError("column name :$idx not found in the data frame; " *
 126                            "existing most similar names are: $candidatesstr"))
 127    end
 24128    i
 129end
 130
 49131@inline Base.getindex(x::Index, idx::AbstractString) = lookupname(x.lookup, idx)
 0132@inline function Base.getindex(x::Index, idx::AbstractVector{AbstractString})
 0133    allunique(idx) || throw(ArgumentError("Elements of $idx must be unique"))
 0134    [lookupname(x.lookup, i) for i in idx]
 135end

Methods/Properties