This repository was archived by the owner on May 5, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathindex.jl
152 lines (133 loc) · 4.53 KB
/
index.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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# an AbstractIndex is a thing that can be used to look up ordered things by name, but that
# will also accept a position or set of positions or range or other things and pass them
# through cleanly.
@compat abstract type AbstractIndex end
type Index <: AbstractIndex # an OrderedDict would be nice here...
lookup::Dict{Symbol, Int} # name => names array position
names::Vector{Symbol}
end
function Index(names::Vector{Symbol}; allow_duplicates=true)
u = make_unique(names, allow_duplicates=allow_duplicates)
lookup = Dict{Symbol, Int}(zip(u, 1:length(u)))
Index(lookup, u)
end
Index() = Index(Dict{Symbol, Int}(), Symbol[])
Base.length(x::Index) = length(x.names)
Base.names(x::Index) = copy(x.names)
_names(x::Index) = x.names
Base.copy(x::Index) = Index(copy(x.lookup), copy(x.names))
Base.deepcopy(x::Index) = copy(x) # all eltypes immutable
Base.isequal(x::Index, y::Index) = isequal(x.lookup, y.lookup) && isequal(x.names, y.names)
# Imported in DataTables.jl for compatibility across Julia 0.4 and 0.5
(==)(x::Index, y::Index) = isequal(x, y)
function names!(x::Index, nms::Vector{Symbol}; allow_duplicates=false)
if length(nms) != length(x)
throw(ArgumentError("Length of nms doesn't match length of x."))
end
newindex = Index(nms, allow_duplicates=allow_duplicates)
x.names = newindex.names
x.lookup = newindex.lookup
return x
end
function rename!(x::Index, nms)
for (from, to) in nms
if haskey(x, to)
error("Tried renaming $from to $to, when $to already exists in the Index.")
end
x.lookup[to] = col = pop!(x.lookup, from)
x.names[col] = to
end
return x
end
rename!(x::Index, from, to) = rename!(x, zip(from, to))
rename!(x::Index, from::Symbol, to::Symbol) = rename!(x, ((from, to),))
rename!(x::Index, f::Function) = rename!(x, [(x,f(x)) for x in x.names])
rename!(f::Function, x::Index) = rename!(x, f)
rename(x::Index, args...) = rename!(copy(x), args...)
rename(f::Function, x::Index) = rename(x, f)
Base.haskey(x::Index, key::Symbol) = haskey(x.lookup, key)
Base.haskey(x::Index, key::Real) = 1 <= key <= length(x.names)
Base.keys(x::Index) = names(x)
# TODO: If this should stay 'unsafe', perhaps make unexported
function Base.push!(x::Index, nm::Symbol)
x.lookup[nm] = length(x) + 1
push!(x.names, nm)
return x
end
function Base.merge!(x::Index, y::Index)
adds = add_names(x, y)
i = length(x)
for add in adds
i += 1
x.lookup[add] = i
end
append!(x.names, adds)
return x
end
Base.merge(x::Index, y::Index) = merge!(copy(x), y)
function Base.delete!(x::Index, idx::Integer)
# reset the lookup's beyond the deleted item
for i in (idx + 1):length(x.names)
x.lookup[x.names[i]] = i - 1
end
delete!(x.lookup, x.names[idx])
deleteat!(x.names, idx)
return x
end
function Base.delete!(x::Index, nm::Symbol)
if !haskey(x.lookup, nm)
return x
end
idx = x.lookup[nm]
return delete!(x, idx)
end
function Base.empty!(x::Index)
empty!(x.lookup)
empty!(x.names)
x
end
function Base.insert!(x::Index, idx::Integer, nm::Symbol)
1 <= idx <= length(x.names)+1 || error(BoundsError())
for i = idx:length(x.names)
x.lookup[x.names[i]] = i + 1
end
x.lookup[nm] = idx
insert!(x.names, idx, nm)
x
end
Base.getindex(x::Index, idx::Symbol) = x.lookup[idx]
Base.getindex(x::AbstractIndex, idx::Real) = @compat Int(idx)
Base.getindex(x::AbstractIndex, idx::AbstractVector{Nullable{Bool}}) =
getindex(x, convert(Vector{Bool}, idx, false))
Base.getindex{T<:Nullable}(x::AbstractIndex, idx::AbstractVector{T}) =
getindex(x, dropnull(idx))
Base.getindex(x::AbstractIndex, idx::AbstractVector{Bool}) = find(idx)
Base.getindex(x::AbstractIndex, idx::Range) = [idx;]
Base.getindex{T <: Real}(x::AbstractIndex, idx::AbstractVector{T}) = convert(Vector{Int}, idx)
Base.getindex(x::AbstractIndex, idx::AbstractVector{Symbol}) = [x.lookup[i] for i in idx]
# Helpers
function add_names(ind::Index, add_ind::Index)
add_names(_names(ind), names(add_ind))
end
function add_names(a::Vector{Symbol}, u::Vector{Symbol})
seen = Set(a)
dups = Int[]
for i in 1:length(u)
name = u[i]
in(name, seen) ? push!(dups, i) : push!(seen, name)
end
for i in dups
nm = u[i]
k = 1
while true
newnm = Symbol("$(nm)_$k")
if !in(newnm, seen)
u[i] = newnm
push!(seen, newnm)
break
end
k += 1
end
end
return u
end