NamedTupleTools.jl
April 3, 2025 · View on GitHub
Some NamedTuple utilities
Copyright © 2015-2025 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
namesandvalues - given a
Dict{Symbol, Any} - given a
struct - given a
VectorofPairs - inversive Reconstruction
Reconstruction
- obtaining
namesandvalues - 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
SymbolsorStrings - The names, values may be
TuplesorVectors
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]
2-element 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)
2-element Array{Pair{Symbol,Int64},1}:
:a => 1
:b => 2
nt = (a = 1, b = "two", c = 3.0);
vec = convert(Vector{Pair}, nt)
3-element 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