From fbe6d28c8ef218d9795f4d9f48515a7ec7f4e4f7 Mon Sep 17 00:00:00 2001 From: Julian P Samaroo Date: Tue, 18 Sep 2018 16:18:46 -0500 Subject: [PATCH 1/7] Added recursive merge function for NamedTuple, addresses #29215 --- base/namedtuple.jl | 12 ++++++++++++ test/namedtuple.jl | 3 +++ 2 files changed, 15 insertions(+) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index 46927b51d89d4..779dddb0903be 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -241,6 +241,18 @@ merge(a::NamedTuple{()}, b::NamedTuple) = b merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge(a, b.data) +""" + merge(a::NamedTuple, b::NamedTuple) + +Perform a recursive merge of two or more named tuples. + +```jldoctest +julia> merge((a=1, b=2), (b=3, c=(f=1)), (c=(f=2),)) +(a = 1, b = 3, c = (d = 2,)) +``` +""" +merge(a::NamedTuple, b::NamedTuple...) = merge(merge(a, b[1]), b[2:end]...) + """ merge(a::NamedTuple, iterable) diff --git a/test/namedtuple.jl b/test/namedtuple.jl index 18395fe2d9508..7e653824ef342 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -240,3 +240,6 @@ y = map(v -> (a=v.a, b=v.a + v.b), [(a=1, b=missing), (a=1, b=2)]) # Iterator constructor @test NamedTuple{(:a, :b), Tuple{Int, Float64}}(Any[1.0, 2]) === (a=1, b=2.0) @test NamedTuple{(:a, :b)}(Any[1.0, 2]) === (a=1.0, b=2) + +# Recursive merge, issue #29215 +@test merge((a=1, b=2), (b=3, c=(d=1,)), (c=(d=2,),)) === (a=1, b=3, c=(d=2,)) From 6a729033d8b157d2c2fdbefe1ae91c6bb8aeab0a Mon Sep 17 00:00:00 2001 From: Julian P Samaroo Date: Tue, 18 Sep 2018 16:37:31 -0500 Subject: [PATCH 2/7] Fixed NamedTuple recursive merge docstring slightly, added another test for said merge function --- base/namedtuple.jl | 2 +- test/namedtuple.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index 779dddb0903be..85934e1177605 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -242,7 +242,7 @@ merge(a::NamedTuple{()}, b::NamedTuple) = b merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge(a, b.data) """ - merge(a::NamedTuple, b::NamedTuple) + merge(a::NamedTuple, b::NamedTuple...) Perform a recursive merge of two or more named tuples. diff --git a/test/namedtuple.jl b/test/namedtuple.jl index 7e653824ef342..ae1fba6094b71 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -242,4 +242,5 @@ y = map(v -> (a=v.a, b=v.a + v.b), [(a=1, b=missing), (a=1, b=2)]) @test NamedTuple{(:a, :b)}(Any[1.0, 2]) === (a=1.0, b=2) # Recursive merge, issue #29215 +@test merge((a=1, b=2), (b=3, c=4), (c=5,)) === (a=1, b=3, c=5) @test merge((a=1, b=2), (b=3, c=(d=1,)), (c=(d=2,),)) === (a=1, b=3, c=(d=2,)) From 2d14ca36aa128bd501092203568aa5b612ba02f5 Mon Sep 17 00:00:00 2001 From: Julian P Samaroo Date: Tue, 18 Sep 2018 21:58:53 -0500 Subject: [PATCH 3/7] Change "recursive" NamedTuple merge to "left-associative" merge, and make definition more elegant --- base/namedtuple.jl | 6 +++--- test/namedtuple.jl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index 85934e1177605..1a63b0a0e8f93 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -242,16 +242,16 @@ merge(a::NamedTuple{()}, b::NamedTuple) = b merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge(a, b.data) """ - merge(a::NamedTuple, b::NamedTuple...) + merge(a::NamedTuple, b::NamedTuple, cs::NamedTuple...) -Perform a recursive merge of two or more named tuples. +Perform a left-associative merge of three or more named tuples. ```jldoctest julia> merge((a=1, b=2), (b=3, c=(f=1)), (c=(f=2),)) (a = 1, b = 3, c = (d = 2,)) ``` """ -merge(a::NamedTuple, b::NamedTuple...) = merge(merge(a, b[1]), b[2:end]...) +merge(a::NamedTuple, b::NamedTuple, cs::NamedTuple...) = merge(merge(a, b), cs...) """ merge(a::NamedTuple, iterable) diff --git a/test/namedtuple.jl b/test/namedtuple.jl index ae1fba6094b71..e7467be85d82b 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -241,6 +241,6 @@ y = map(v -> (a=v.a, b=v.a + v.b), [(a=1, b=missing), (a=1, b=2)]) @test NamedTuple{(:a, :b), Tuple{Int, Float64}}(Any[1.0, 2]) === (a=1, b=2.0) @test NamedTuple{(:a, :b)}(Any[1.0, 2]) === (a=1.0, b=2) -# Recursive merge, issue #29215 +# Left-associative merge, issue #29215 @test merge((a=1, b=2), (b=3, c=4), (c=5,)) === (a=1, b=3, c=5) @test merge((a=1, b=2), (b=3, c=(d=1,)), (c=(d=2,),)) === (a=1, b=3, c=(d=2,)) From 4202ec9c08fc64e58f626f361552e60ee8f2c369 Mon Sep 17 00:00:00 2001 From: Julian P Samaroo Date: Tue, 18 Sep 2018 22:01:22 -0500 Subject: [PATCH 4/7] Fixed jldoctest failure --- base/namedtuple.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index 1a63b0a0e8f93..c19223b9687f4 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -247,7 +247,7 @@ merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge Perform a left-associative merge of three or more named tuples. ```jldoctest -julia> merge((a=1, b=2), (b=3, c=(f=1)), (c=(f=2),)) +julia> merge((a=1, b=2), (b=3, c=(f=1,)), (c=(f=2,),)) (a = 1, b = 3, c = (d = 2,)) ``` """ From 8d4ac08259bc01f9658a5d37cde755632a00ec29 Mon Sep 17 00:00:00 2001 From: Julian P Samaroo Date: Wed, 19 Sep 2018 10:07:46 -0500 Subject: [PATCH 5/7] Re-fixify the docstring... --- base/namedtuple.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index c19223b9687f4..8c39176663de3 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -247,7 +247,7 @@ merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge Perform a left-associative merge of three or more named tuples. ```jldoctest -julia> merge((a=1, b=2), (b=3, c=(f=1,)), (c=(f=2,),)) +julia> merge((a=1, b=2), (b=3, c=(d=1,)), (c=(d=2,),)) (a = 1, b = 3, c = (d = 2,)) ``` """ From 1a8d4c4c4cf944583f23ef326b541eed0b0864d7 Mon Sep 17 00:00:00 2001 From: Julian P Samaroo Date: Sat, 20 Oct 2018 16:50:27 -0500 Subject: [PATCH 6/7] Combine NamedTuple merge docstrings --- base/namedtuple.jl | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index 8c39176663de3..9515f01feef97 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -213,16 +213,23 @@ end end """ - merge(a::NamedTuple, b::NamedTuple) + merge(a::NamedTuple, bs::NamedTuple...) -Construct a new named tuple by merging two existing ones. -The order of fields in `a` is preserved, but values are taken from matching -fields in `b`. Fields present only in `b` are appended at the end. +Construct a new named tuple by merging two or more existing ones, in a left-associative +manner. Merging proceeds left-to-right, between pairs of named tuples, and so the order of fields +present in both the leftmost and rightmost named tuples take the same position as they are found in the +leftmost named tuple. However, values are taken from matching fields in the rightmost named tuple that +contains that field. Fields present in only the rightmost named tuple of a pair are appended at the end. ```jldoctest julia> merge((a=1, b=2, c=3), (b=4, d=5)) (a = 1, b = 4, c = 3, d = 5) ``` + +```jldoctest +julia> merge((a=1, b=2), (b=3, c=(d=1,)), (c=(d=2,),)) +(a = 1, b = 3, c = (d = 2,)) +``` """ function merge(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn} if @generated @@ -241,16 +248,6 @@ merge(a::NamedTuple{()}, b::NamedTuple) = b merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge(a, b.data) -""" - merge(a::NamedTuple, b::NamedTuple, cs::NamedTuple...) - -Perform a left-associative merge of three or more named tuples. - -```jldoctest -julia> merge((a=1, b=2), (b=3, c=(d=1,)), (c=(d=2,),)) -(a = 1, b = 3, c = (d = 2,)) -``` -""" merge(a::NamedTuple, b::NamedTuple, cs::NamedTuple...) = merge(merge(a, b), cs...) """ From 3d9a9550391ae8e7c436c4da13f7f0dd7210e8d4 Mon Sep 17 00:00:00 2001 From: Julian P Samaroo Date: Sun, 21 Oct 2018 06:00:09 -0500 Subject: [PATCH 7/7] Added single argument merge --- base/namedtuple.jl | 4 ++++ test/namedtuple.jl | 1 + 2 files changed, 5 insertions(+) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index 9515f01feef97..2f9bc8cb39f86 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -220,6 +220,8 @@ manner. Merging proceeds left-to-right, between pairs of named tuples, and so th present in both the leftmost and rightmost named tuples take the same position as they are found in the leftmost named tuple. However, values are taken from matching fields in the rightmost named tuple that contains that field. Fields present in only the rightmost named tuple of a pair are appended at the end. +A fallback is implemented for when only a single named tuple is supplied, +with signature `merge(a::NamedTuple)`. ```jldoctest julia> merge((a=1, b=2, c=3), (b=4, d=5)) @@ -250,6 +252,8 @@ merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge merge(a::NamedTuple, b::NamedTuple, cs::NamedTuple...) = merge(merge(a, b), cs...) +merge(a::NamedTuple) = a + """ merge(a::NamedTuple, iterable) diff --git a/test/namedtuple.jl b/test/namedtuple.jl index e7467be85d82b..b88e7d5663e3e 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -244,3 +244,4 @@ y = map(v -> (a=v.a, b=v.a + v.b), [(a=1, b=missing), (a=1, b=2)]) # Left-associative merge, issue #29215 @test merge((a=1, b=2), (b=3, c=4), (c=5,)) === (a=1, b=3, c=5) @test merge((a=1, b=2), (b=3, c=(d=1,)), (c=(d=2,),)) === (a=1, b=3, c=(d=2,)) +@test merge((a=1, b=2)) === (a=1, b=2)