Skip to content

Commit

Permalink
Merge 8d80e40 into 529ced1
Browse files Browse the repository at this point in the history
  • Loading branch information
bramtayl committed Dec 7, 2018
2 parents 529ced1 + 8d80e40 commit 28e4c42
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 44 deletions.
2 changes: 1 addition & 1 deletion REQUIRE
@@ -1,2 +1,2 @@
julia 0.7
Indexing
Indexing
2 changes: 1 addition & 1 deletion src/SplitApplyCombine.jl
Expand Up @@ -36,6 +36,6 @@ include("invert.jl")
# Silly definitions missing from Base
# ===================================
# this should always work
Base.haskey(a, i) = i keys(a)
Base.haskey(a, i) = i keys(a)

end # module
14 changes: 5 additions & 9 deletions src/innerjoin.jl
@@ -1,14 +1,8 @@
# Join works on collections of collections (e.g. a table is a collection of
# rows).

innerjoin(left, right) = innerjoin(identity, identity, left, right)
innerjoin(lkey, rkey, left, right) = innerjoin(lkey, rkey, merge, left, right)
innerjoin(lkey, rkey, f, left, right) = innerjoin(lkey, rkey, f, isequal, left, right)

const = innerjoin

"""
innerjoin(lkey, rkey, f, comparison, left, right)
innerjoin(left, right; lkey = identity, rkey = lkey, f = tuple, comparison = isequal)
Performs a relational-style join operation between iterables `left` and `right`, returning
a collection of elements `f(l, r)` for which `comparison(lkey(l), rkey(r))` is `true` where
Expand All @@ -17,7 +11,7 @@ a collection of elements `f(l, r)` for which `comparison(lkey(l), rkey(r))` is `
# Example
```jldoctest
julia> innerjoin(iseven, iseven, tuple, ==, [1,2,3,4], [0,1,2])
julia> innerjoin([1,2,3,4], [0,1,2], lkey = iseven)
6-element Array{Tuple{Int64,Int64},1}:
(1, 1)
(2, 0)
Expand All @@ -27,7 +21,7 @@ julia> innerjoin(iseven, iseven, tuple, ==, [1,2,3,4], [0,1,2])
(4, 2)
```
"""
function innerjoin(lkey, rkey, f, comparison, left, right)
function innerjoin(left, right; lkey = identity, rkey = lkey, f = tuple, comparison = isequal)
# TODO Do this inference-free, like comprehensions...
T = promote_op(f, eltype(left), eltype(right))
out = empty(left, T)
Expand All @@ -36,6 +30,8 @@ function innerjoin(lkey, rkey, f, comparison, left, right)
return out
end

const = innerjoin

function innerjoin!(out, lkey, rkey, f, comparison, left, right)
# The O(length(left)*length(right)) generic method when nothing about `comparison` is known
for a left
Expand Down
43 changes: 21 additions & 22 deletions src/leftgroupjoin.jl
@@ -1,9 +1,5 @@
leftgroupjoin(left, right) = leftgroupjoin(identity, identity, left, right)
leftgroupjoin(lkey, rkey, left, right) = leftgroupjoin(lkey, rkey, merge, left, right)
leftgroupjoin(lkey, rkey, f, left, right) = leftgroupjoin(lkey, rkey, f, isequal, left, right)

"""
leftgroupjoin(lkey, rkey, f, comparison, left, right)
leftgroupjoin(left, right; lkey = identity, rkey = lkey, f = tuple, comparison = isequal)
Creates a collection if groups labelled by `lkey(l)` where each group contains elements
`f(l, r)` which satisfy `comparison(lkey(l), rkey(r))`. If there are no matches, the group
Expand All @@ -14,33 +10,36 @@ This operation shares some similarities with an SQL left outer join.
### Example
```jldoctest
julia> leftgroupjoin(iseven, iseven, tuple, ==, [1,2,3,4], [0,1,2])
julia> leftgroupjoin([1,2,3,4], [0,1,2], lkey = iseven)
Dict{Bool,Array{Tuple{Int64,Int64},1}} with 2 entries:
false => Tuple{Int64,Int64}[(1, 1), (3, 1)]
true => Tuple{Int64,Int64}[(2, 0), (2, 2), (4, 0), (4, 2)]
```
"""
function leftgroupjoin(lkey, rkey, f, comparison, left, right)
function leftgroupjoin(left, right; lkey = identity, rkey = lkey, f = tuple, comparison = isequal)
# The O(length(left)*length(right)) generic method when nothing about `comparison` is known

# TODO Do this inference-free, like comprehensions...
T = promote_op(f, eltype(left), eltype(right))
K = promote_op(lkey, eltype(left))
V = Vector{T}
out = Dict{K, V}()
for a left
key = lkey(a)
group = get!(() -> T[], out, key)
for b right
if comparison(lkey(a), rkey(b))
push!(group, f(a, b))
if isa(comparison, typeof(isequal))
leftgroupjoin_hash(lkey, rkey, f, comparison, left, right)
else
# TODO Do this inference-free, like comprehensions...
T = promote_op(f, eltype(left), eltype(right))
K = promote_op(lkey, eltype(left))
V = Vector{T}
out = Dict{K, V}()
for a left
key = lkey(a)
group = get!(() -> T[], out, key)
for b right
if comparison(lkey(a), rkey(b))
push!(group, f(a, b))
end
end
end
return out
end
return out
end

function leftgroupjoin(lkey, rkey, f, ::typeof(isequal), left, right)
function leftgroupjoin_hash(lkey, rkey, f, ::typeof(isequal), left, right)
# isequal heralds a hash-based approach, roughly O(length(left) * log(length(right)))

# TODO Do this inference-free, like comprehensions...
Expand Down Expand Up @@ -68,7 +67,7 @@ function leftgroupjoin(lkey, rkey, f, ::typeof(isequal), left, right)
return out
end

function leftgroupjoin(lkey, rkey, f, ::typeof(isequal), left::AbstractArray, right::AbstractArray)
function leftgroupjoin_hash(lkey, rkey, f, ::typeof(isequal), left::AbstractArray, right::AbstractArray)
# isequal heralds a hash-based approach, roughly O(length(left) * log(length(right)))

# TODO Do this inference-free, like comprehensions...
Expand Down
12 changes: 6 additions & 6 deletions test/innerjoin.jl
Expand Up @@ -2,16 +2,16 @@
@testset "UnitRange" begin
l = 2:5
r = 3:6
@test innerjoin(identity, identity, tuple, l, r) == [(3,3), (4,4), (5,5)]
@test innerjoin(identity, identity, tuple, isequal, l, r) == [(3,3), (4,4), (5,5)]
@test innerjoin(identity, identity, tuple, ==, l, r) == [(3,3), (4,4), (5,5)]

@test innerjoin(identity, identity, tuple, isless, l, r) == [(2,3), (2,4), (2,5), (2,6), (3,4), (3,5), (3,6), (4,5), (4,6), (5,6)]
@test innerjoin(l, r) == [(3,3), (4,4), (5,5)]
@test innerjoin(l, r, comparison = (==)) == [(3,3), (4,4), (5,5)]

@test innerjoin(l, r, comparison = isless) == [(2,3), (2,4), (2,5), (2,6), (3,4), (3,5), (3,6), (4,5), (4,6), (5,6)]
end

@testset "Arrays of NamedTuple" begin
l = [(a=1, b=2.0), (a=2, b=4.0), (a=3, b=6.0)]
r = [(a=1, c=:a), (a=2, c=:b), (a=4,c=:d)]
@test innerjoin(x->x.a, x->x.a, l, r) == [(a=1, b=2.0, c=:a), (a=2, b=4.0, c=:b)]
@test innerjoin(l, r, lkey = x -> x.a, f = merge) == [(a=1, b=2.0, c=:a), (a=2, b=4.0, c=:b)]
end
end
end
10 changes: 5 additions & 5 deletions test/leftgroupjoin.jl
Expand Up @@ -4,12 +4,12 @@
ans1 = Dict(1 => [(1,1)],
2 => [(2,2)],
3 => [],
4 => [])
4 => [])
ans2 = Dict(false => [(1, 1), (3, 1)],
true => [(2, 0), (2, 2), (4, 0), (4, 2)])


@test leftgroupjoin(identity, identity, tuple, l ,r) == ans1
@test leftgroupjoin(iseven, iseven, tuple, l, r) == ans2
@test leftgroupjoin(iseven, iseven, tuple, ==, l, r) == ans2
end
@test leftgroupjoin(l, r) == ans1
@test leftgroupjoin(l, r, lkey = iseven) == ans2
@test leftgroupjoin(l, r, lkey = iseven, comparison = (==)) == ans2
end

0 comments on commit 28e4c42

Please sign in to comment.