# Making an iterator that splats keys and values quickly into kwargs

**URL:** <https://discourse.julialang.org/t/making-an-iterator-that-splats-keys-and-values-quickly-into-kwargs/89357>\
**Category:** New to Julia\
**Tags:** question\
**Created:** [October 27, 2022, 10:57am UTC](https://discourse.julialang.org/t/making-an-iterator-that-splats-keys-and-values-quickly-into-kwargs/89357 "2022-10-27T10:57:49Z")\
**Posts on this page:** 1\
**Showing post:** 17

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [November 1, 2022, 11:53pm UTC](https://discourse.julialang.org/t/making-an-iterator-that-splats-keys-and-values-quickly-into-kwargs/89357/17 "2022-11-01T23:53:37Z")

</div>

Oh wow.

So trying this approach to make one-line solutions for the original problem, using a `Generator` becomes hugely slow! What the heck! The same approach which previously was quite performant, is now painfully slow.

Setup:

```julia
using BenchmarkTools
struct MyStruct{K1,V1,K2,V2}
    a::NamedTuple{K1,V1}
    b::NamedTuple{K2,V2}
end
x = MyStruct((a=1, b=2, d=4, e=5, f=6), (b=3, c=4))

```

First, making one-line solutions using `map` and `filter` to set a speed baseline:

```julia
Base.keys(x::MyStruct{K1,<:Any,K2,<:Any}) where {K1,K2} = 
    (K1..., filter(k->k ∉ K1, K2)...)
Base.values(x::MyStruct{K1,<:Any,K2,<:Any}) where {K1,K2} = 
    (values(x.a)..., map(Base.Fix1(getfield, x.b), filter(k->k ∉ K1, K2))...)
Base.merge(nt::NamedTuple, x::MyStruct{K1,<:Any,K2,<:Any}) where {K1,K2} = 
    (; nt..., NamedTuple{keys(x)}(values(x))...)

```

Runtime with `map` and `filter`:

```julia
julia> @btime keys($x)
  1.000 ns (0 allocations: 0 bytes)
(:a, :b, :d, :e, :f, :c)

julia> @btime values($x)
  2.300 ns (0 allocations: 0 bytes)
(1, 2, 4, 5, 6, 4)

julia> @btime (; $x...)
  2.600 ns (0 allocations: 0 bytes)
(a = 1, b = 2, d = 4, e = 5, f = 6, c = 4)

```

Nice and fast.

Okay, now trying one-line solutions using generators, code is almost identical:

```julia
Base.keys(x::MyStruct{K1,<:Any,K2,<:Any}) where {K1,K2} = 
    (K1..., (k for k ∈ K2 if k ∉ K1)...)
Base.values(x::MyStruct{K1,<:Any,K2,<:Any}) where {K1,K2} = 
    (values(x.a)..., (getfield(x.b,k) for k ∈ K2 if k ∉ K1)...)
Base.merge(nt::NamedTuple, x::MyStruct{K1,<:Any,K2,<:Any}) where {K1,K2} = 
    (; nt..., NamedTuple{keys(x)}(values(x))...)

```

Runtime with Generators:

```julia
julia> @btime keys($x)
  309.045 ns (2 allocations: 96 bytes)
(:a, :b, :d, :e, :f, :c)

julia> @btime values($x)
  410.326 ns (5 allocations: 256 bytes)
(1, 2, 4, 5, 6, 4)

julia> @btime (; $x...)
  1.480 μs (11 allocations: 560 bytes)
(a = 1, b = 2, d = 4, e = 5, f = 6, c = 4)

```

Oof! Generators are slow af!

If I’m missing something, I don’t see it… it just seems like generators sometimes just become slow without rhyme or reason.

Considering that generators are themselves just syntax sugar for `map` and `filter` over iterators, this shouldn’t be the case?

---

_[View the full topic](https://discourse.julialang.org/t/making-an-iterator-that-splats-keys-and-values-quickly-into-kwargs/89357)._
