|
| 1 | +# This file is a part of Julia. License is MIT: https://julialang.org/license |
| 2 | + |
| 3 | +""" |
| 4 | + NamedTuple{names,T}(args::Tuple) |
| 5 | +
|
| 6 | +Construct a named tuple with the given `names` (a tuple of Symbols) and field types `T` |
| 7 | +(a `Tuple` type) from a tuple of values. |
| 8 | +""" |
| 9 | +function NamedTuple{names,T}(args::Tuple) where {names, T <: Tuple} |
| 10 | + if length(args) == length(names) |
| 11 | + if @generated |
| 12 | + N = length(names) |
| 13 | + types = T.parameters |
| 14 | + Expr(:new, :(NamedTuple{names,T}), Any[ :(convert($(types[i]), args[$i])) for i in 1:N ]...) |
| 15 | + else |
| 16 | + N = length(names) |
| 17 | + NT = NamedTuple{names,T} |
| 18 | + types = T.parameters |
| 19 | + fields = Any[ convert(types[i], args[i]) for i = 1:N ] |
| 20 | + ccall(:jl_new_structv, Any, (Any, Ptr{Void}, UInt32), NT, fields, N)::NT |
| 21 | + end |
| 22 | + else |
| 23 | + throw(ArgumentError("Wrong number of arguments to named tuple constructor.")) |
| 24 | + end |
| 25 | +end |
| 26 | + |
| 27 | +""" |
| 28 | + NamedTuple{names}(args::Tuple) |
| 29 | +
|
| 30 | +Construct a named tuple with the given `names` (a tuple of Symbols) from a tuple of |
| 31 | +values. |
| 32 | +""" |
| 33 | +function NamedTuple{names}(args::Tuple) where {names} |
| 34 | + NamedTuple{names,typeof(args)}(args) |
| 35 | +end |
| 36 | + |
| 37 | +""" |
| 38 | + NamedTuple{names}(nt::NamedTuple) |
| 39 | +
|
| 40 | +Construct a named tuple by selecting fields in `names` (a tuple of Symbols) from |
| 41 | +another named tuple. |
| 42 | +""" |
| 43 | +function NamedTuple{names}(nt::NamedTuple) where {names} |
| 44 | + if @generated |
| 45 | + types = Tuple{(fieldtype(nt, n) for n in names)...} |
| 46 | + Expr(:new, :(NamedTuple{names, $types}), Any[ :(getfield(nt, $(QuoteNode(n)))) for n in names ]...) |
| 47 | + else |
| 48 | + types = Tuple{(fieldtype(typeof(nt), n) for n in names)...} |
| 49 | + NamedTuple{names, types}(Tuple(getfield(nt, n) for n in names)) |
| 50 | + end |
| 51 | +end |
| 52 | + |
| 53 | +NamedTuple() = NamedTuple{(),Tuple{}}(()) |
| 54 | + |
| 55 | +length(t::NamedTuple) = nfields(t) |
| 56 | +start(t::NamedTuple) = 1 |
| 57 | +done(t::NamedTuple, iter) = iter > nfields(t) |
| 58 | +next(t::NamedTuple, iter) = (getfield(t, iter), iter + 1) |
| 59 | +endof(t::NamedTuple) = nfields(t) |
| 60 | +getindex(t::NamedTuple, i::Int) = getfield(t, i) |
| 61 | +getindex(t::NamedTuple, i::Symbol) = getfield(t, i) |
| 62 | +indexed_next(t::NamedTuple, i::Int, state) = (getfield(t, i), i+1) |
| 63 | + |
| 64 | +convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names,T}) where {names,T} = nt |
| 65 | +convert(::Type{NamedTuple{names}}, nt::NamedTuple{names}) where {names} = nt |
| 66 | + |
| 67 | +function convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names}) where {names,T} |
| 68 | + NamedTuple{names,T}(T(nt)) |
| 69 | +end |
| 70 | + |
| 71 | +function show(io::IO, t::NamedTuple) |
| 72 | + n = nfields(t) |
| 73 | + for i = 1:n |
| 74 | + # if field types aren't concrete, show full type |
| 75 | + if typeof(getfield(t, i)) !== fieldtype(typeof(t), i) |
| 76 | + show(io, typeof(t)) |
| 77 | + print(io, "(") |
| 78 | + show(io, Tuple(t)) |
| 79 | + print(io, ")") |
| 80 | + return |
| 81 | + end |
| 82 | + end |
| 83 | + if n == 0 |
| 84 | + print(io, "NamedTuple()") |
| 85 | + else |
| 86 | + print(io, "(") |
| 87 | + for i = 1:n |
| 88 | + print(io, fieldname(typeof(t),i), " = "); show(io, getfield(t, i)) |
| 89 | + if n == 1 |
| 90 | + print(io, ",") |
| 91 | + elseif i < n |
| 92 | + print(io, ", ") |
| 93 | + end |
| 94 | + end |
| 95 | + print(io, ")") |
| 96 | + end |
| 97 | +end |
| 98 | + |
| 99 | +eltype(::Type{NamedTuple{names,T}}) where {names,T} = eltype(T) |
| 100 | + |
| 101 | +==(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = Tuple(a) == Tuple(b) |
| 102 | +==(a::NamedTuple, b::NamedTuple) = false |
| 103 | + |
| 104 | +isequal(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isequal(Tuple(a), Tuple(b)) |
| 105 | +isequal(a::NamedTuple, b::NamedTuple) = false |
| 106 | + |
| 107 | +_nt_names(::NamedTuple{names}) where {names} = names |
| 108 | +_nt_names(::Type{T}) where {names,T<:NamedTuple{names}} = names |
| 109 | + |
| 110 | +hash(x::NamedTuple, h::UInt) = xor(object_id(_nt_names(x)), hash(Tuple(x), h)) |
| 111 | + |
| 112 | +isless(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isless(Tuple(a), Tuple(b)) |
| 113 | +# TODO: case where one argument's names are a prefix of the other's |
| 114 | + |
| 115 | +same_names(::NamedTuple{names}...) where {names} = true |
| 116 | +same_names(::NamedTuple...) = false |
| 117 | + |
| 118 | +function map(f, nt::NamedTuple{names}, nts::NamedTuple...) where names |
| 119 | + if !same_names(nt, nts...) |
| 120 | + throw(ArgumentError("Named tuple names do not match.")) |
| 121 | + end |
| 122 | + # this method makes sure we don't define a map(f) method |
| 123 | + NT = NamedTuple{names} |
| 124 | + if @generated |
| 125 | + N = length(names) |
| 126 | + M = length(nts) |
| 127 | + args = Expr[:(f($(Expr[:(getfield(nt, $j)), (:(getfield(nts[$i], $j)) for i = 1:M)...]...))) for j = 1:N] |
| 128 | + :( NT(($(args...),)) ) |
| 129 | + else |
| 130 | + NT(map(f, map(Tuple, (nt, nts...))...)) |
| 131 | + end |
| 132 | +end |
| 133 | + |
| 134 | +# a version of `in` for the older world these generated functions run in |
| 135 | +@pure function sym_in(x::Symbol, itr::Tuple{Vararg{Symbol}}) |
| 136 | + for y in itr |
| 137 | + y === x && return true |
| 138 | + end |
| 139 | + return false |
| 140 | +end |
| 141 | + |
| 142 | +@pure function merge_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}}) |
| 143 | + names = Symbol[an...] |
| 144 | + for n in bn |
| 145 | + if !sym_in(n, an) |
| 146 | + push!(names, n) |
| 147 | + end |
| 148 | + end |
| 149 | + (names...,) |
| 150 | +end |
| 151 | + |
| 152 | +@pure function merge_types(names::Tuple{Vararg{Symbol}}, a::Type{<:NamedTuple}, b::Type{<:NamedTuple}) |
| 153 | + bn = _nt_names(b) |
| 154 | + Tuple{Any[ fieldtype(sym_in(n, bn) ? b : a, n) for n in names ]...} |
| 155 | +end |
| 156 | + |
| 157 | +""" |
| 158 | + merge(a::NamedTuple, b::NamedTuple) |
| 159 | +
|
| 160 | +Construct a new named tuple by merging two existing ones. |
| 161 | +The order of fields in `a` is preserved, but values are taken from matching |
| 162 | +fields in `b`. Fields present only in `b` are appended at the end. |
| 163 | +
|
| 164 | +```jldoctest |
| 165 | +julia> merge((a=1, b=2, c=3), (b=4, d=5)) |
| 166 | +(a = 1, b = 4, c = 3, d = 5) |
| 167 | +``` |
| 168 | +""" |
| 169 | +function merge(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn} |
| 170 | + if @generated |
| 171 | + names = merge_names(an, bn) |
| 172 | + types = merge_types(names, a, b) |
| 173 | + vals = Any[ :(getfield($(sym_in(n, bn) ? :b : :a), $(QuoteNode(n)))) for n in names ] |
| 174 | + :( NamedTuple{$names,$types}(($(vals...),)) ) |
| 175 | + else |
| 176 | + names = merge_names(an, bn) |
| 177 | + types = merge_types(names, typeof(a), typeof(b)) |
| 178 | + NamedTuple{names,types}(map(n->getfield(sym_in(n, bn) ? b : a, n), names)) |
| 179 | + end |
| 180 | +end |
| 181 | + |
| 182 | +merge(a::NamedTuple{()}, b::NamedTuple) = b |
| 183 | + |
| 184 | +""" |
| 185 | + merge(a::NamedTuple, iterable) |
| 186 | +
|
| 187 | +Interpret an iterable of key-value pairs as a named tuple, and perform a merge. |
| 188 | +
|
| 189 | +```jldoctest |
| 190 | +julia> merge((a=1, b=2, c=3), [:b=>4, :d=>5]) |
| 191 | +(a = 1, b = 4, c = 3, d = 5) |
| 192 | +``` |
| 193 | +""" |
| 194 | +function merge(a::NamedTuple, itr) |
| 195 | + names = Symbol[] |
| 196 | + vals = Any[] |
| 197 | + inds = ObjectIdDict() |
| 198 | + for (k,v) in itr |
| 199 | + oldind = get(inds, k, 0) |
| 200 | + if oldind > 0 |
| 201 | + vals[oldind] = v |
| 202 | + else |
| 203 | + push!(names, k) |
| 204 | + push!(vals, v) |
| 205 | + inds[k] = length(names) |
| 206 | + end |
| 207 | + end |
| 208 | + merge(a, NamedTuple{(names...,)}((vals...,))) |
| 209 | +end |
| 210 | + |
| 211 | +keys(nt::NamedTuple{names}) where {names} = names |
| 212 | +values(nt::NamedTuple) = Tuple(nt) |
| 213 | +haskey(nt::NamedTuple, key::Union{Integer, Symbol}) = isdefined(nt, key) |
| 214 | +get(nt::NamedTuple, key::Union{Integer, Symbol}, default) = haskey(nt, key) ? getfield(nt, key) : default |
| 215 | +get(f::Callable, nt::NamedTuple, key::Union{Integer, Symbol}) = haskey(nt, key) ? getfield(nt, key) : f() |
0 commit comments