NamedTupleTools.jl
Some NamedTuple utilities
Copyright © 20152023 by Jeffrey Sarnoff. This work is released under The MIT License.
Overview
NamedTuples
are built from fieldnames, given as Symbols
and field values, as they may be given.
These utilities make some uses of NamedTuples
more straightforward. This package benefits greatly
from others (see Credits).
Operations
Construction
 given
names
andvalues
 given a
Dict{Symbol, Any}
 given a
struct
 given a
Vector
ofPairs
 inversive Reconstruction
Reconstruction
 obtaining
names
andvalues
 obtaining a
Dict{Symbol, Any}
 obtaining a
struct
 obtaining a
Vector{Pair}
 inversive Construction
Selection
 select one or more named constituents
 complements Deletion
Deletion
 delete one or more named constituents
 complements Selection
Merging
Recursive Merging
Splitting
Functions
Construction from names and values
julia> using NamedTupleTools
julia> namesofvalues = (:instrument, :madeby)
julia> matchingvalues = ("violin", "Stradivarius")
julia> nt = namedtuple(namesofvalues, matchingvalues)
(instrument = "violin", madeby = "Stradivarius")
 The names may be given as
Symbols
orStrings
 The names, values may be
Tuples
orVectors
Selecting Aspects of Elements
julia> using NamedTupleTools
julia> nt = NamedTuple{(:a, :b)}(1.0, "two")
(a = 1.0, b = "two")
julia> typeof(nt) == NamedTuple{(:a, :b),Tuple{Float64,String}}
true
julia> propertynames(nt) == (:a, :b)
true
julia> fieldnames(nt) == (:a, :b) # synonym for the moment
true
julia> fieldtypes(nt) == (Float64, String)
true
julia> valtype(nt) == Tuple{Float64, String}
true
julia> fieldvalues(nt) == (1.0, "two")
true
Use NamedTuple prototypes
using NamedTupleTools
julia> namedtuple(:a, :b, :c)(1, 2.0, "three")
(a = 1, b = 2.0, c = "three")
#=
namedtuple( name1, name2, .. )
namedtuple( (name1, name2, ..) )
where the `names` are all `Symbols` or all `Strings`
Generate a NamedTuple prototype by specifying or obtaining the fieldnames.
The prototype is applied to fieldvalues, giving a completed NamedTuple.
=#
julia> nt = (a = 1, b = "two")
(a = 1, b = "two")
julia> nt_prototype = prototype(nt)
NamedTuple{(:a, :b),T} where T<:Tuple
julia> nt_prototype = namedtuple(:a, :b)
NamedTuple{(:a, :b),T} where T<:Tuple
julia> nt = nt_prototype(1, 2)
(a = 1, b = 2)
julia> nt = nt_prototype("A", 3)
(a = "A", b = 3)
julia> isprototype(nt_prototype)
true
julia> isprototype(nt)
false
Select
using NamedTupleTools
julia> nt = (a = 1, b = 2, y = 25, z = 26)
(a = 1, b = 2, y = 25, z = 26)
julia> ay = select(nt, (:a, :y))
(a = 1, y = 25)
Delete
using NamedTupleTools
julia> ntproto = namedtuple( :a, :b, :c );
NamedTuple{(:a, :b, :c),T} where T<:Tuple
julia> delete(ntproto, :b) === namedtuple(:a, :c)
true
julia> fieldnames(delete(ntproto, :b))
NamedTuple{(:a, :c),T} where T<:Tuple
julia> fieldnames(delete(ntproto, (:a, :c)), fieldnames(delete(ntproto, :a, :c)
(:b,), (:b,)
julia> nt = ntproto(1, 2, 3)
(a = 1, b = 2, c = 3)
julia> delete(nt, :a)
(b = 2, c = 3)
julia> delete(nt, :a, :c)
(b = 2,)
Merge
# merge from 2..7 NamedTuples
julia> ntproto1 = namedtuple(:a, :b);
julia> ntproto2 = namedtuple(:b, :c);
julia> merge(ntproto1, ntproto2)
NamedTuple{(:a, :b, :c),T} where T<:Tuple
julia> nt1 = (a = 3, b = 5);
julia> nt2 = (c = 8,);
julia> merge(nt1, nt2)
(a = 3, b = 5, c = 8)
julia> nt1 = (a = 3, b = 5);
julia> nt2 = (b = 6, c = 8);
julia> merge(nt1, nt2)
(a = 3, b = 6, c = 8)
recursive_merge
#=
Recursively merge namedtuples. Where more than one of the namedtuple args share the same fieldname (same key),
the leftmost argument's key's value will be propogated. Where each namedtuple has distinct fieldnames (keys),
all of named fields will be gathered with their respective values. The named fields will appear in the same
order they are encountered (leftmost arg, second leftmost arg, .., second rightmost arg, rightmost arg).
If there are no nested namedtuples, `merge(nt1, nts..., recursive=true)` is the same as `merge(nt1, nts...)`.
=#
a = (food = (fruits = (orange = "mango", white = "pear"),
liquids = (water = "still", wine = "burgandy")))
b = (food = (fruits = (yellow = "banana", orange = "papaya"),
liquids = (water = "sparkling", wine = "champagne"),
bread = "multigrain"))
merge(b,a) == (fruits = (orange = "mango", white = "pear"),
liquids = (water = "still", wine = "burgandy"),
bread = "multigrain")
merge_recursive(b,a) ==
(fruits = (yellow = "banana", orange = "mango", white = "pear"),
liquids = (water = "still", wine = "burgandy"),
bread = "multigrain")
merge(a,b) == (fruits = (yellow = "banana", orange = "papaya"),
liquids = (water = "sparkling", wine = "champagne"),
bread = "multigrain")
merge_recursive(a,b) ==
(fruits = (orange = "papaya", white = "pear", yellow = "banana"),
liquids = (water = "sparkling", wine = "champagne"),
bread = "multigrain")
Split
julia> using NamedTupleTools
julia> nt = (a = 1, b = 2, c = 3, d = 4);
julia> split(nt, :a)
((a = 1,), (b = 2, c = 3, d = 4))
julia> split(nt, (:a, :b))
((a = 1, b = 2), (c = 3, d = 4))
julia> merge(split(nt, (:a, :b))...) == nt
true
Struct construction, conversion
using NamedTupleTools
julia> struct MyStruct
tally::Int
team::String
end
julia> mystruct = MyStruct(5, "hometeam")
MyStruct(5, "hometeam")
julia> mynamedtuple = ntfromstruct(mystruct)
(tally = 5, team = "hometeam")
julia> ntstruct = structfromnt(MyStruct, mynamedtuple)
MyStruct(5, "hometeam")
julia> mystruct == ntstruct
true
AbstractDict construction, reconstruction
julia> nt = (a = 1, b = 2)
(a = 1, b = 2)
julia> convert(Dict, nt)
Dict{Symbol,Int64} with 2 entries:
:a => 1
:b => 2
julia> adict = Dict(:a => 1, :b => "two")
Dict{Symbol,Any} with 2 entries:
:a => 1
:b => "two"
julia> nt = namedtuple(adict)
(a = 1, b = "two")
julia> convert(Dict, nt)
Dict{Symbol,Union{Int64, String}} with 2 entries:
:a => 1
:b => "two"
julia> nt = namedtuple(adict)
(a = 1, b = 2//11, c = "three")
julia> convert(Dict, nt)
Dict{Symbol,Union{Rational{Int64}, Int64, String}} with 3 entries:
:a => 1
:b => 2//11
:c => "three"
julia> using OrderedCollections: OrderedDict, LittleDict
julia> ldict = OrderedDict(:a => 1, :b => "two")
OrderedDict{Symbol,Any} with 2 entries:
:a => 1
:b => "two"
julia> nt = namedtuple(ldict)
(a = 1, b = "two")
julia> convert(LittleDict, nt)
LittleDict{Symbol,Union{Int64, String},Array{Symbol,1},Array{Union{Int64, String},1}} with 2 entries:
:a => 1
:b => "two"
Vector of Pairs
julia> vec = [:a => 1, :b => 2]
2element Array{Pair{Symbol,Int64},1}:
:a => 1
:b => 2
julia> nt = namedtuple(vec)
(a = 1, b = 2)
convert to Vector Of Pairs
julia> nt = (a=1, b=2);
julia> convert(Vector{Pair}, nt)
2element Array{Pair{Symbol,Int64},1}:
:a => 1
:b => 2
nt = (a = 1, b = "two", c = 3.0);
vec = convert(Vector{Pair}, nt)
3element Array{Pair{Symbol,B} where B,1}:
:a => 1
:b => "two"
:c => 3.0
Variables mixed with standard syntax
julia> a, b, c, d, f = 1, 1.0, 1//1, "one", (g=1,)
(1, 1.0, 1//1, "one", (g = 1,))
julia> nt = @namedtuple(a, b, c, d, e = a + b, f...)
(a = 1, b = 1.0, c = 1//1, d = "one", e = 2.0, g = 1)
Credits

Construction from names and values
 submitted by Kristoffer Carlsson

Use NamedTuple prototypes
 improved by Chad Scherrer

Select
 submitted by Chad Scherrer

Split
 submitted by Seth Axen

AbstractDict construction, reconstruction
 improved by Kevin Squire

Vector of Pairs
 submitted by Peter Deffebach

Variables mixed with standard syntax
 submitted by Sebastian Pfitzner, Takafumi Arakaki

Delete, Select: inferencing, coverage
 improved by Gustavo Goretkin

Merge: support recursive merging
 submitted by @wytbella

fixups
 remove ambiguity
 from @marius311
 remove unused parameter
 from Neven Sajko
 remove ambiguity