@unpack with Parameters.jl when key is not found

Hi, is there an easy way to have Parameters.jl skip missing keys or fields?

using Parameters
mydict = Dict(a=>3,b=>2.3,c=>"mystr")
@unpack (a,b,c,d) = mydict
ERROR: KeyError: key "d" not found

I would like it to just skip over the missing key. The reason I want this behavior is I am carrying around two lists which hold fixed parameters and ones to be found via optimization. It would be nice if I could just unpack them, without having to check which ones were in which list like:

@unpack (fixed1,fixed2,...) = fixedParams
@unpack (fitted1,fitted2,...) fittedParams

and instead being able to just say

@unpack (p1,p2,...) = fixedParams
@unpack (p1,p2,...) = fittedParams

I don’t think so. Part of the reason @unpack is fast is because it doesn’t have these kinds of checks. A macro doesn’t know what’s inside a Dict.

1 Like

Is there a way to write my own method then that does this check? It doesn’t need to be fast.

UnPack.jl is actually super simple once you understand metaprogramming.

In particular, lines 32-36 are what you would want to change. Instead of getindex you would want get(d, x, nothing) or similar.


I tried adding both of the following methods:

@inline unpack(x::AbstractDict{<:AbstractString}, k) = get(x,string(k),nothing)
@inline unpack(x::AbstractDict{<:AbstractString}, ::Val{k}) where {k} = get(x,string(k),nothing)

but still got the same error:

@unpack (a,b,c,d) = test
ERROR: KeyError: key "d" not found

Could you please tell us more about what steps you did to add the methods? Did you add the package for development using ] dev?

Ah sorry. I thought I was able to add methods similar to how I can add methods in Base.jl. So I was just adding them inline in my Julia script.

You can, but you should import methods first.
But this is type piracy and shouldn’t be used if you are writing a library (but more or less fine in your local script)

Here is an MWE

julia> using UnPack

julia> import UnPack: unpack

help?> unpack
search: unpack UnPack @unpack

  This function is invoked to unpack one field/entry of some DataType dt and has signature:

  unpack(dt::Any, ::Val{sym}) -> value of sym

  The sym is the symbol of the assigned variable.

  Three definitions are included in the package to unpack a composite type or a dictionary with Symbol
  or string keys:

  @inline unpack(x, ::Val{f}) where {f} = getproperty(x, f)
  @inline unpack(x::AbstractDict{Symbol}, ::Val{k}) where {k} = x[k]
  @inline unpack(x::AbstractDict{S}, ::Val{k}) where {S<:AbstractString,k} = x[string(k)]

  More methods can be added to allow for specialized unpacking of other datatypes.

  See also pack!.

julia> unpack(x::AbstractDict{S}, ::Val{k}) where {S<:AbstractString,k} = get(x, string(k), nothing)
unpack (generic function with 3 methods)

julia> d = Dict(["a" => 1])
Dict{String, Int64} with 1 entry:
  "a" => 1

julia> @unpack a, c = d
Dict{String, Int64} with 1 entry:
  "a" => 1

julia> c

That said, don’t do this… Maybe you can define your own macro, use @unpack's parsing, but call your own function instead of unpack.

1 Like