# Collect an iterator into an SVector or Tuple

**URL:** <https://discourse.julialang.org/t/collect-an-iterator-into-an-svector-or-tuple/38812>\
**Category:** General Usage\
**Tags:** question\
**Created:** [May 5, 2020, 2:56pm UTC](https://discourse.julialang.org/t/collect-an-iterator-into-an-svector-or-tuple/38812 "2020-05-05T14:56:51Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 5, 2020, 2:56pm UTC](https://discourse.julialang.org/t/collect-an-iterator-into-an-svector-or-tuple/38812/1 "2020-05-05T14:56:51Z")

</div>

Is there a recommended idiom for collecting a number of elements (known at compile time, embedded in some type parameter) from an iterable into a `Tuple` or `SVector`?

I can write a generated function and unroll, but I was hoping for something simple and clean. MWE:

```julia
using StaticArrays
struct Fibonacci end
function Base.iterate(::Fibonacci, state = (0, 1))
    f1, f2 = state
    f0 = f1 + f2
    f0, (f0, f1)
end
Base.IteratorEltype(::Type{Fibonacci}) = Base.HasEltype()
Base.eltype(::Type{Fibonacci}) = Int
Base.IteratorSize(::Type{Fibonacci}) = Base.IsInfinite()

# want the result here, without collecting first
SVector{5}(collect(Iterators.take(Fibonacci(), 5)))

```

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [May 5, 2020, 3:13pm UTC](https://discourse.julialang.org/t/collect-an-iterator-into-an-svector-or-tuple/38812/2 "2020-05-05T15:13:58Z")

</div>

Here’s my attempt. Given an iterator `x`, we can generate a tuple by repeatedly calling `iterate` and taking the values:

```julia
julia> x = [1, 2, 3];

julia> (((val, state) = iterate(x); val), ((val, state) = iterate(x, state); val), ((val, state) = iterate(x, state); val))
(1, 2, 3)

```

Here’s a macro that automates that process:

```julia
julia> macro tuple_unpack(N::Int, x)
         @assert N >= 1
         expr = Expr(:tuple)
         push!(expr.args, quote
           begin
             (val, state) = iterate($(esc(x)))
             val
           end
         end)
         for i = 2:N
           push!(expr.args, quote
             begin
               (val, state) = iterate($(esc(x)), state)
               val
             end
           end)
         end
         expr
       end
@tuple_unpack (macro with 1 method)

```

Usage:

```julia
julia> x = [1, 2, 3];

julia> @tuple_unpack 3 x
(1, 2, 3)

julia> g = (2 * i for i in 1:4)
Base.Generator{UnitRange{Int64},var"#13#14"}(var"#13#14"(), 1:4)

julia> @tuple_unpack 4 g
(2, 4, 6, 8)

```

Performance seems good, at least for a very simple case:

```julia
julia> f(x) = @tuple_unpack 3 x
f (generic function with 1 method)

julia> @btime f($x)
  6.606 ns (0 allocations: 0 bytes)
(1, 2, 3)

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 5, 2020, 3:38pm UTC](https://discourse.julialang.org/t/collect-an-iterator-into-an-svector-or-tuple/38812/3 "2020-05-05T15:38:51Z")

</div>

Thanks, I am looking for something that does not involve metaprogramming.

Behold

```julia
function static_take(::Val{K}, itr) where K
    K == 0 && error("so how would I know the type?")
    dummy = SVector(ntuple(_ -> nothing, Val(K + 1)))
    function f(::Nothing, ::Nothing)
        x, state = iterate(itr)
        SVector(x), state
    end
    function f((xs, state), ::Nothing)
        x, state = iterate(itr, state)
        push(xs, x), state
    end
    xs, _ = foldl(f, dummy)
    SVector(xs)
end

static_take(Val(5), Fibonacci())

```

which is not going to win any beauty contests, but it infers (but of course in a way this is cheating, since `StaticArrays` does the code generation).

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [May 5, 2020, 6:30pm UTC](https://discourse.julialang.org/t/collect-an-iterator-into-an-svector-or-tuple/38812/4 "2020-05-05T18:30:27Z")

</div>

You can do

```julia
using BangBang: push!!
foldl(push!!, itr; init=SA[])

```

which can be generalized to work with arbitrary (well-behaving) array type:

```julia
using BangBang: push!!, Empty
collectto(T::Type, itr) = foldl(push!!, itr; init=Empty(T))

```

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [May 5, 2020, 6:31pm UTC](https://discourse.julialang.org/t/collect-an-iterator-into-an-svector-or-tuple/38812/5 "2020-05-05T18:31:28Z")

</div>

Actually, if you just need it for `SVector`, maybe the easiest thing to do is:

```julia
mapfoldl(x -> SA[x], vcat, itr; init=SA[])

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 6, 2020, 8:25am UTC](https://discourse.julialang.org/t/collect-an-iterator-into-an-svector-or-tuple/38812/6 "2020-05-06T08:25:22Z")

</div>

This is indeed easy, but it does not infer the size. Eg

```julia
take2(::Val{K}, itr) where K = mapfoldl(x -> SA[x], vcat, Iterators.take(itr, K); init=SA[])
take2(Val{5}(), Fibonacci())

```

infers as `Any`.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 6, 2020, 8:41am UTC](https://discourse.julialang.org/t/collect-an-iterator-into-an-svector-or-tuple/38812/7 "2020-05-06T08:41:42Z")

</div>

I just realized that I can use a `Tuple` for counting (and nothing else), resulting in this nice example of compiler abuse.

```julia
_rec(acc, itr, state, token1) = acc
function _rec(acc, itr, state, token1, tokens...)
    x, state′ = iterate(itr, state)
    _rec((acc..., x), itr, state′, tokens...)
end
function take3(cnt, itr)
    r = iterate(itr)
    r ≡ nothing && return ()
    _rec((first(r), ), itr, last(r), ntuple(_ -> nothing, cnt)...)
end

@code_warntype take3(Val{5}(), Fibonacci()) # infers

```
