Repository: JuliaCollections/DataStructures.jl (julia)

Issue:
Absence of merge() in RobinDict
The absence of `merge` method call for RobinDict leads to the following problem. 
```julia
julia> using DataStructures

julia> rd1 = RobinDict()
RobinDict{Any,Any} with 0 entries

julia> for i=1:10
           rd1[i] = 2*i
       end

julia> rd2 = RobinDict()
RobinDict{Any,Any} with 0 entries

julia> for i=11:20
           rd2[i] = 2*i + 1
       end

julia> merge(rd1, rd2)
Dict{Any,Any} with 20 entries:
  18 => 37
  2  => 4
  16 => 33
  11 => 23
  7  => 14
  9  => 18
  10 => 20
  19 => 39
  17 => 35
  8  => 16
  6  => 12
  4  => 8
  3  => 6
  5  => 10
  20 => 41
  13 => 27
  14 => 29
  15 => 31
  12 => 25
  1  => 2
```
`merge(rd1, rd2)` should have been of the type `RobinDict`.

Pull request description:
Fixes #626

Patch:
diff --git a/src/robin_dict.jl b/src/robin_dict.jl
index 44dc68c73..6fa31851d 100644
--- a/src/robin_dict.jl
+++ b/src/robin_dict.jl
@@ -1,5 +1,5 @@
 import Base: setindex!, sizehint!, empty!, isempty, length, copy, empty,
-             getindex, getkey, haskey, iterate, @propagate_inbounds,
+             getindex, getkey, haskey, iterate, @propagate_inbounds, merge,
              pop!, delete!, get, get!, isbitstype, in, hashindex, isbitsunion,
              isiterable, dict_with_eltype, KeySet, Callable, _tablesz, filter!
 
@@ -610,3 +610,22 @@ end
 @propagate_inbounds iterate(t::RobinDict, i) = _iterate(t, get_next_filled(t, i))
 
 filter!(f, d::RobinDict) = Base.filter_in_one_pass!(f, d)
+
+function _merge_kvtypes(d, others...)
+    K, V = keytype(d), valtype(d)
+    for other in others
+        K = promote_type(K, keytype(other))
+        V = promote_type(V, valtype(other))
+    end
+    return (K, V)
+end
+
+function merge(d::RobinDict, others::AbstractDict...)
+    K, V = _merge_kvtypes(d, others...)
+    merge!(RobinDict{K,V}(), d, others...)
+end
+
+function merge(combine::Function, d::RobinDict, others::AbstractDict...)
+    K, V = _merge_kvtypes(d, others...)
+    merge!(combine, RobinDict{K,V}(), d, others...)
+end


Test patch:
diff --git a/test/test_robin_dict.jl b/test/test_robin_dict.jl
index 9ddbf8c1d..cb09631fa 100644
--- a/test/test_robin_dict.jl
+++ b/test/test_robin_dict.jl
@@ -359,6 +359,28 @@ end
     @test h.idxfloor == get_idxfloor(h) == 0
 end
 
+@testset "merge" begin
+    h1 = RobinDict("a" => 1, "b" => 2)
+    h2 = RobinDict("c" => 3, "d" => 4)
+    d = merge(h1, h2)
+    @test isa(d, RobinDict{String, Int})
+    @test length(d) == 4
+    @test d["a"] == 1
+    @test d["b"] == 2
+    @test d["c"] == 3
+    @test d["d"] == 4
+end
+
+@testset "merge with combine function" begin
+    h1 = RobinDict("a" => 1, "b" => 2)
+    h2 = RobinDict("b" => 3, "c" => 4)
+    d = merge(+, h1, h2)
+    @test isa(d, RobinDict{String, Int})
+    @test d["b"] == 5
+    @test d["a"] == 1
+    @test d["c"] == 4
+end
+
 @testset "invariants" begin
     h1 = RobinDict{Int, Int}()
     for i in 1:300