Skip to content

Commit

Permalink
Migrate API to use equalto, following JuliaLang/julia#23812
Browse files Browse the repository at this point in the history
  • Loading branch information
garrison committed Oct 14, 2017
1 parent f4b28bf commit a5b1238
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 53 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@
julia> ia[1]
"cat"

julia> findfirst(ia, "dog") # executes quickly via a dictionary lookup, not sequential search
julia> findfirst(equalto("dog"), ia) # executes quickly via a dictionary lookup, not sequential search
2

(Note that on julia 0.6, one must specify `using Compat` to bring `equalto` into the current namespace.)

As might be expected, `UniqueVector` supports many of the usual methods for `Vector`, but all operations enforce the condition that each element of the array must be unique. The mutating methods `push!`, `pop!`, and `empty!` are implemented as well, as these operations keep constant the indices of existing elements in the array, allowing the dictionary to be updated efficiently.

In addition, `UniqueVector` implements a mutating `findfirst!` method, which returns the index of an element if it exists in the array, or otherwise appends the element and returns its new index:

julia> findfirst!(ia, "cat")
julia> findfirst!(equalto("cat"), ia)
1

julia> findfirst!(ia, "horse")
julia> findfirst!(equalto("horse"), ia)
4

julia> ia
Expand Down
1 change: 1 addition & 0 deletions REQUIRE
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
julia 0.6
Compat 0.33.0
44 changes: 28 additions & 16 deletions src/UniqueVectors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ __precompile__()

module UniqueVectors

using Compat

include("delegate.jl")

import Base: copy, in, getindex, findfirst, findlast, length, size, isempty, start, done, next, empty!, push!, pop!, setindex!, indexin, findin, findnext, findprev
Expand Down Expand Up @@ -45,44 +47,54 @@ end
in(item::T, ia::UniqueVector{T}) where {T} = haskey(ia.lookup, item)
in(item, ia::UniqueVector{T}) where {T} = in(convert(T, item), ia)

findfirst(ia::UniqueVector{T}, item::T) where {T} =
get(ia.lookup, item, 0)
findfirst(p::equalto{<:T}, ia::UniqueVector{T}) where {T} =
get(ia.lookup, p.x, 0)

function findfirst!(ia::UniqueVector{T}, item::T) where {T}
rv = get!(ia.lookup, item) do
function findfirst!(p::equalto{<:T}, ia::UniqueVector{T}) where {T}
rv = get!(ia.lookup, p.x) do
# NOTE: does not provide any exception safety guarantee
push!(ia.items, item)
push!(ia.items, p.x)
return length(ia.items)
end
@assert length(ia.items) == length(ia.lookup)
return rv
end

findfirst(ia::UniqueVector{T}, item) where {T} =
findfirst(ia, convert(T, item))
findfirst(p::equalto, ia::UniqueVector{T}) where {T} =
findfirst(equalto(convert(T, p.x)), ia)

findfirst!(p::equalto, ia::UniqueVector{T}) where {T} =
findfirst!(equalto(convert(T, p.x)), ia)

findlast(p::equalto, ia::UniqueVector) =
findfirst(p, ia)

findfirst!(ia::UniqueVector{T}, item) where {T} =
findfirst!(ia, convert(T, item))
@deprecate findfirst(ia::UniqueVector, item) findfirst(equalto(item), ia)

findlast(ia::UniqueVector, item) =
findfirst(ia, item)
@deprecate findfirst!(ia::UniqueVector, item) findfirst!(equalto(item), ia)

@deprecate findlast(ia::UniqueVector, item) findlast(equalto(item), ia)

indexin(a::AbstractArray, b::UniqueVector) =
[findfirst(b, elt) for elt in a]
[findfirst(equalto(elt), b) for elt in a]

findin(a, b::UniqueVector) =
[i for (i, ai) in enumerate(a) if ai b]

function findnext(A::UniqueVector, v, i::Integer)
idx = findfirst(A, v)
function findnext(p::equalto, A::UniqueVector, i::Integer)
idx = findfirst(p, A)
idx >= i ? idx : 0
end

function findprev(A::UniqueVector, v, i::Integer)
idx = findfirst(A, v)
@deprecate findnext(A::UniqueVector, v, i::Integer) findnext(equalto(v), A, i)

function findprev(p::equalto, A::UniqueVector, i::Integer)
idx = findfirst(p, A)
idx <= i ? idx : 0
end

@deprecate findprev(A::UniqueVector, v, i::Integer) findprev(equalto(v), A, i)

function push!(ia::UniqueVector{T}, item::T) where {T}
if item in ia
throw(ArgumentError("cannot add duplicate item to UniqueVector"))
Expand Down
70 changes: 36 additions & 34 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
using UniqueVectors
using Base.Test

using Compat

@test length(UniqueVector([1,5,6,3])) == 4
@test_throws ArgumentError UniqueVector([1,3,5,6,3])

ia = UniqueVector{String}()

@test isempty(ia)
@test_throws ArgumentError pop!(ia)
@test findfirst(ia, "cat") == 0
@test findfirst!(ia, "cat") == 1
@test findfirst(equalto("cat"), ia) == 0
@test findfirst!(equalto("cat"), ia) == 1
@test !isempty(ia)
@test "cat" in ia
@test "dog" ia
@test findfirst!(ia, "dog") == 2
@test findfirst!(ia, "cat") == 1
@test findfirst!(ia, "mouse") == 3
@test findfirst!(ia, "dog") == 2
@test findfirst(ia, "cat") == 1
@test findlast(ia, "cat") == 1
@test findfirst(ia, "dog") == 2
@test findfirst(ia, "mouse") == 3
@test findfirst!(equalto("dog"), ia) == 2
@test findfirst!(equalto("cat"), ia) == 1
@test findfirst!(equalto("mouse"), ia) == 3
@test findfirst!(equalto("dog"), ia) == 2
@test findfirst(equalto("cat"), ia) == 1
@test findlast(equalto("cat"), ia) == 1
@test findfirst(equalto("dog"), ia) == 2
@test findfirst(equalto("mouse"), ia) == 3
@test ia[1] == "cat"
@test ia[2] == "dog"
@test ia[3] == "mouse"
Expand All @@ -30,8 +32,8 @@ ia = UniqueVector{String}()
@test endof(ia) == 3

ia2 = UniqueVector([1, 2, 3])
@test findfirst(ia2, 0x02) == 2
@test findfirst!(ia2, 0x02) == 2
@test findfirst(equalto(0x02), ia2) == 2
@test findfirst!(equalto(0x02), ia2) == 2
@test ia2 == UniqueVector(i for i in 1:3)
for elt in [3,2,1]
@test pop!(ia2) == elt
Expand All @@ -43,19 +45,19 @@ ia2 = copy(ia)

@test empty!(ia) === ia
@test isempty(ia)
@test findfirst(ia, "cat") == 0
@test findfirst!(ia, "horse") == 1
@test findfirst(equalto("cat"), ia) == 0
@test findfirst!(equalto("horse"), ia) == 1
@test_throws ArgumentError push!(ia, "horse")
@test length(ia) == 1
@test push!(ia, "human") === ia
@test findfirst(ia, "human") == 2
@test findfirst(equalto("human"), ia) == 2
@test pop!(ia) == "human"
@test length(ia) == 1
@test ia[:] == ["horse"]
@test findfirst(ia, "human") == 0
@test findfirst(equalto("human"), ia) == 0

@test ia2[:] == ["cat", "dog", "mouse"]
@test findfirst(ia2, "cat") == 1
@test findfirst(equalto("cat"), ia2) == 1

let ia = UniqueVector(["cat", "dog", "mouse", "human"]), original = copy(ia)

Expand All @@ -66,21 +68,21 @@ let ia = UniqueVector(["cat", "dog", "mouse", "human"]), original = copy(ia)

swap!(ia, 2, 3)

@test findfirst(ia, "mouse") == 2
@test findfirst(original, "mouse") == 3
@test findfirst(equalto("mouse"), ia) == 2
@test findfirst(equalto("mouse"), original) == 3

@test findfirst(ia, "dog") == 3
@test findfirst(original, "dog") == 2
@test findfirst(equalto("dog"), ia) == 3
@test findfirst(equalto("dog"), original) == 2
end

@test UniqueVector([1,2,3,4]) == UniqueVector(1:4)

# Test it works with `Any` datatype
let ia3 = UniqueVector([1,"cat",2,"dog"])
@test eltype(ia3) == Any
@test findfirst(ia3, 1) == 1
@test findfirst!(ia3, "dog") == 4
@test findfirst!(ia3, "horse") == 5
@test findfirst(equalto(1), ia3) == 1
@test findfirst!(equalto("dog"), ia3) == 4
@test findfirst!(equalto("horse"), ia3) == 5
end

# Test setindex!
Expand All @@ -101,19 +103,19 @@ push!(ia5, 3)
ia5[1] = 4
@test ia5[:] == [4.0]
@test 4 ia5
@test findfirst(ia5, 4) == 1
@test findlast(ia5, 4) == 1
@test findfirst(equalto(4), ia5) == 1
@test findlast(equalto(4), ia5) == 1

# Test indexin and findin
@test indexin([1,2,34,0,5,56], UniqueVector([34,56,35,1,5,0])) == [4,0,1,6,5,2]
@test findin([1,2,34,0,5,56], UniqueVector([34,56,35,1,5,0])) == [1,3,4,5,6]

# Test findnext and findprev
@test findnext(UniqueVector([3,5,7,9]), 7, 1) == 3
@test findnext(UniqueVector([3,5,7,9]), 7, 2) == 3
@test findnext(UniqueVector([3,5,7,9]), 7, 3) == 3
@test findnext(UniqueVector([3,5,7,9]), 7, 4) == 0
@test findprev(UniqueVector([3,5,7,9]), 7, 1) == 0
@test findprev(UniqueVector([3,5,7,9]), 7, 2) == 0
@test findprev(UniqueVector([3,5,7,9]), 7, 3) == 3
@test findprev(UniqueVector([3,5,7,9]), 7, 4) == 3
@test findnext(equalto(7), UniqueVector([3,5,7,9]), 1) == 3
@test findnext(equalto(7), UniqueVector([3,5,7,9]), 2) == 3
@test findnext(equalto(7), UniqueVector([3,5,7,9]), 3) == 3
@test findnext(equalto(7), UniqueVector([3,5,7,9]), 4) == 0
@test findprev(equalto(7), UniqueVector([3,5,7,9]), 1) == 0
@test findprev(equalto(7), UniqueVector([3,5,7,9]), 2) == 0
@test findprev(equalto(7), UniqueVector([3,5,7,9]), 3) == 3
@test findprev(equalto(7), UniqueVector([3,5,7,9]), 4) == 3

0 comments on commit a5b1238

Please sign in to comment.