# Unpacking a named tuple using field names and slurping?

**URL:** <https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035>\
**Category:** General Usage\
**Created:** [June 8, 2023, 4:31am UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035 "2023-06-08T04:31:41Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![onetonfoot](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/onetonfoot/32/2697_2.png) [@onetonfoot](https://discourse.julialang.org/u/onetonfoot)\
**Post date:** [June 8, 2023, 4:31am UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/1 "2023-06-08T04:31:42Z")

</div>

I’ve got a named tuple with a bunch of fields and I’d like to unpack certain fields based on the key and slurp the remaining.

```julia

r = (x=1, y=2, z=3)
(y, rest...) = r

```

The trouble with the above is that it will unpack based on the order of fields in the named tuple rather than the keys so, the above would return

```julia
y = 1
rest = (y = 2, z = 3)

```

However, I’m essentially looking for something that would give me the same behaviour as the javascript code

```js
const r = {x: 1, y: 2, z: 3}
const {y, ...rest} = r

```

Where the result is

```js
y = 2
rest = {x: 1, z: 3}

```

What would be the succinct way to write this in Julia?

---

<div class="post-metadata">

**Author:** ![onetonfoot](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/onetonfoot/32/2697_2.png) [@onetonfoot](https://discourse.julialang.org/u/onetonfoot)\
**Post date:** [June 8, 2023, 4:45am UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/2 "2023-06-08T04:45:00Z")

</div>

The best I can come up with is

```julia
r = (x=1, y=2, z=3)
idx = [:y]
y = r[idx]
rest = r[InvertedIndex(idx)]

```

But it’s not ideal as I’ve still got some duplication with the idx and variable name.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [June 8, 2023, 8:19am UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/3 "2023-06-08T08:19:23Z")

</div>

Try this:

```julia
using Accessors

(;y) = r
rest = @delete r.y # can use InvertedIndex here as you did, but @delete is type-stable

```

Don’t think it’s possible to get rid of the remaining duplication.

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [June 8, 2023, 9:52am UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/4 "2023-06-08T09:52:37Z")

</div>

Using a quick anonymous function:

```julia
y, rest = ((;y,kwargs...)->(y,(;kwargs...)))(;r...)

```

But the important bit is to check compilation optimizes everything away (which I haven’t for this solution, but aplavin’s does), as this operation is mainly compile-time assignment.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [June 8, 2023, 12:57pm UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/5 "2023-06-08T12:57:52Z")

</div>

You could use a macro.

```julia
julia> macro alajs(e)
           pos=findfirst(el -> startswith(string(el),string(e.args[1].args[1])), e.args[2].args)
           e.args[2].args[pos], e.args[2].args[1] = e.args[2].args[1], e.args[2].args[pos]
           return esc(e);
       end
@alajs (macro with 1 method)

julia> @alajs (y, rest...) = (x=1, y=2, z=3)
(y = 2, x = 1, z = 3)

julia> y
2

julia> rest
(x = 1, z = 3)

```

If someone explains how to expand within the macro the symbol r in the namedtuple that defines it, it would also be valid for expressions like:

`@alajs (y,rest...)=r`

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [June 8, 2023, 1:22pm UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/6 "2023-06-08T13:22:54Z")

</div>

> [@rocco\_sprmnt21](#):
>
> If someone explains how to expand within the macro the symbol r in the namedtuple that defines it

You can’t. What the macro needs to do is to generate code which at _runtime_ can manipulate the contents of `r`.

But yes, it’s definitely possible to implement a macro which allows you to do

```julia
@alajs (y,rest...)=r

```

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [June 8, 2023, 2:18pm UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/7 "2023-06-08T14:18:56Z")

</div>

Combined with an earlier suggestion, but with not a shred of sanity or error checking:

```julia
macro alajs(ex)
    unpacked = ex.args[1].args[1:(end - 1)]
    rest = ex.args[1].args[end].args[1]
    rhs = ex.args[2]
    return esc(quote
        $(unpacked...), $rest = ((;$(unpacked...), $(rest)...) -> ($(unpacked...), (;($rest)...)))(;$(rhs)...)
    end)
end

```

Testing it:

```julia
julia> @alajs (y, z, rest...) = r
(2, 3, (x = 1,))

julia> y
2

julia> z
3

julia> rest
(x = 1,)

```

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [June 8, 2023, 2:37pm UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/8 "2023-06-08T14:37:09Z")

</div>

```julia
macro alajs(e)
    nms = [propertynames(eval(e.args[2]))...]
    vls=[values(eval(e.args[2]))...]
    pos=findfirst(el -> string(el)== string(e.args[1].args[1]), nms)
    nms[1],nms[pos]=nms[pos],nms[1]
    vls[1],vls[pos]=vls[pos],vls[1]
    return esc(:($(e.args[1])=$((;zip(nms,vls)...))))
end

```

is that okay?  
I wrote the last expression tentatively, without fully understanding the syntax.

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [June 8, 2023, 3:23pm UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/9 "2023-06-08T15:23:47Z")

</div>

No, `eval` will happen in global scope when the macro is expanded, so you can’t really use that macro inside a function.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [June 9, 2023, 8:17pm UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/10 "2023-06-09T20:17:41Z")

</div>

I struggled a lot to make it work, but in the end I think it’s decent.

```julia
julia> macro alajs(ex)
           nm = ex.args[1].args[1:(end - 1)]
           rest = ex.args[1].args[end].args[1]
           r = ex.args[2]
       esc(quote
           function swap($(r))
               ($NamedTuple{$(nm...,)}($r)...,Base.structdiff($r,($NamedTuple{$(nm...,)}($r))))
           end
               $(nm...),$(rest)= swap($(r))
           end)
       end
@alajs (macro with 1 method)

julia> @alajs (y, rest...) = r
(2, (x = 1, z = 3))

julia> @alajs (z, rest...) = r
(3, (x = 1, y = 2))

julia> @alajs (z, x, rest...) = r
(3, 1, (y = 2,))

julia> @alajs (z, x,y, rest...) = r
(3, 1, 2, NamedTuple())

```

Compared to the other, it would have the advantage of having specific error indications for NamedTuple

```julia
julia> macro alajs(ex)
           unpacked = ex.args[1].args[1:(end - 1)]
           rest = ex.args[1].args[end].args[1]
           rhs = ex.args[2]
           return esc(quote
               $(unpacked...), $rest = ((;$(unpacked...), $(rest)...) -> ($(unpacked...), (;$rest...)))(;$(rhs)...)
           end)
       end
@alajs (macro with 1 method)

julia> @alajs (z, x,y,x, rest...) = r
ERROR: syntax: function argument name not unique: "x" around c:\Users\sprmn\.julia\environments

julia> @alajs (z, x,y,w, rest...) = r
ERROR: UndefKeywordError: keyword argument `w` not assigned

julia> macro alajs(ex)
           nm = ex.args[1].args[1:(end - 1)]
           rest = ex.args[1].args[end].args[1]
           r = ex.args[2]
       esc(quote
           function swap($(r))
               ($NamedTuple{$(nm...,)}($r)...,Base.structdiff($r,$NamedTuple{$(nm...,)}($r)))
           end
               $(nm...),$(rest)= swap($(r))
           end)
       end
@alajs (macro with 1 method)

julia> @alajs (z, x,y,w, rest...) = r
ERROR: type NamedTuple has no field w

julia> @alajs (z, x,y,x, rest...) = r
ERROR: duplicate field name in NamedTuple: "x" is not unique

```

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [June 9, 2023, 8:55pm UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/11 "2023-06-09T20:55:15Z")

</div>

```julia
macro alajs(ex)
    nm = ex.args[1].args[1:(end - 1)]
    rest = ex.args[1].args[end].args[1]
    r = ex.args[2]
esc(quote
    function swap($(r))
        $(:dn)=setdiff(propertynames($r),$(nm))
        ($NamedTuple{$(nm...,)}($r)...,$NamedTuple{($(:dn)...,)}($r))
    end 
        $(nm...),$(rest)= swap($(r))
    end)
end

```

```julia
macro alajs(ex)
    nm = ex.args[1].args[1:(end - 1)]
    rest = ex.args[1].args[end].args[1]
    r = ex.args[2]
esc(quote
 $(nm...),$(rest)= $NamedTuple{$(nm...,)}($r)..., NamedTuple{filter(x -> x ∉ $nm, propertynames($r))}($r)
    end)
end

```

or

```julia
julia> y,z,rest=r.y,r.z, NamedTuple{(:x,)}(r)
(2, 3, (x = 1,))

```

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [June 9, 2023, 9:59pm UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/12 "2023-06-09T21:59:18Z")

</div>

The versions with `swap` have the following drawback:

```julia
julia> function f(r)
           @alajs (y, r...) = r
           @alajs (z, r...) = r
           return y, z
       end
WARNING: Method definition swap(Any) in module Main at REPL[1]:6 overwritten on the same line.
f (generic function with 1 method)

julia> f(r)
ERROR: type NamedTuple has no field z

```

The one without looks fine. At that point the tradeoff is between (somewhat) graceful errors and speed.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [June 10, 2023, 5:24am UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/13 "2023-06-10T05:24:17Z")

</div>

```julia
julia> macro alajs(ex)
             nm = ex.args[1].args[1:(end - 1)]
             rest = ex.args[1].args[end].args[1]
             r = ex.args[2]
             fn = Symbol("swap" * "_" * join(nm,"_"))
         esc(quote
             function $(fn)($(r))
                 ($NamedTuple{$(nm...,)}($r)...,Base.structdiff($r,$NamedTuple{$(nm...,)}($r)))
             end
                 $(nm...),$(rest)= $(fn)($(r))
             end)
         end
@alajs (macro with 1 method)

julia> function f(r)
        @alajs (y, r...) = r
        @alajs (z, x, r...) = r

        return x, y, z
       end
f (generic function with 1 method)

julia> f(r)
(1, 2, 3)

```

all the solutions proposed here seem to have this problem.  
what exactly happens?  
how could it be overcome?

```julia
julia> function f(r)
        @alajs (y, r...) = r
        @alajs (z, x, r...) = r
#-----
        @alajs (y, x, r...) = r
#-----      
        return x, y, z
       end
f (generic function with 1 method)

julia> f(r)
ERROR: type NamedTuple has no field y

```

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [June 10, 2023, 6:32am UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/14 "2023-06-10T06:32:28Z")

</div>

Print what you have left in `r` after each line there and it should be clear why you can’t extract the `y` field (or any other field) a second time.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [June 10, 2023, 7:21am UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/15 "2023-06-10T07:21:22Z")

</div>

```julia
julia> macro alajs(ex)
           nm = ex.args[1].args[1:(end - 1)]
           rest = ex.args[1].args[end].args[1]
           r = ex.args[2]
       esc(quote
        $(nm...),$(rest)= NamedTuple{$(nm...,)}($r)..., NamedTuple{filter(x -> x ∉ $nm, propertynames($r))}($r)
           end)
       end
@alajs (macro with 1 method)
# My concern was that the namedtupel r was not being consumed by the macro.
# Instead I had inadvertently put r in place of rst in the unpacking expression.
# this is the correct form of the function
julia> 
       function f(r)
        @alajs (y, rst...) = r
        @alajs (z, x, rst...) = r
        @alajs (y, x, rst...) = r
        return x, y, z
       end
f (generic function with 1 method)

julia> f(r)
(1, 2, 3)

```

---

<div class="post-metadata">

**Author:** ![Sevi](https://avatars.discourse-cdn.com/v4/letter/s/c67d28/32.png) [@Sevi](https://discourse.julialang.org/u/Sevi)\
**Post date:** [June 11, 2023, 6:24am UTC](https://discourse.julialang.org/t/unpacking-a-named-tuple-using-field-names-and-slurping/100035/16 "2023-06-11T06:24:07Z")

</div>

Do you think this would be a useful addition to [UnPack.jl](https://github.com/mauro3/UnPack.jl) ?
