# Type-stable zip to pairs

**URL:** <https://discourse.julialang.org/t/type-stable-zip-to-pairs/3390>\
**Category:** General Usage\
**Tags:** question\
**Created:** [April 26, 2017, 6:50pm UTC](https://discourse.julialang.org/t/type-stable-zip-to-pairs/3390 "2017-04-26T18:50:23Z")\
**Posts on this page:** 5\
**Page:** 1

<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:** [April 26, 2017, 6:50pm UTC](https://discourse.julialang.org/t/type-stable-zip-to-pairs/3390/1 "2017-04-26T18:50:23Z")

</div>

I have an interesting puzzle that I haven’t been able solve (without resorting to `@generated` functions).

I’d like to write a function `pairzip(x::Tuple, y::Tuple)` such that

```julia
pairzip((a, b), (c, d)) == (a=>c, b=>d)

```

in a type-stable way, even when the types of a, b, c, and d may all be different. This is easy to do with a generated function:

```julia
@generated function pairzip(x::Tuple{Vararg{Any, N}}, y::Tuple{Vararg{Any, N}}) where {N}
    Expr(:tuple, [:(x[$i] => y[$i]) for i in 1:N]...)
end

```

but I’m curious if there’s another way. Surely there must be some recursive trick I’m missing 😉

Any ideas?

---

<div class="post-metadata">

**Author:** ![fengyang.wang](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fengyang.wang/32/104_2.png) [@fengyang.wang](https://discourse.julialang.org/u/fengyang.wang)\
**Post date:** [April 26, 2017, 7:34pm UTC](https://discourse.julialang.org/t/type-stable-zip-to-pairs/3390/2 "2017-04-26T19:34:36Z")

</div>

```julia
julia> pairzip(t::Tuple{}, u::Tuple{}) = ()
pairzip (generic function with 1 method)

julia> pairzip(t::Tuple{}, u::Tuple) = throw(ArgumentError("args must be equal length"))
pairzip (generic function with 2 methods)

julia> pairzip(t::Tuple, u::Tuple) = (t[1] => u[1], pairzip(Base.tail(t), Base.tail(u))...)
pairzip (generic function with 3 methods)

julia> pairzip((1, 2), (3, 4))
(1=>3, 2=>4)

julia> @code_warntype pairzip((1, 2), (3, 4))
Variables:
  #self#::#pairzip
  t::Tuple{Int64,Int64}
  u::Tuple{Int64,Int64}

Body:
  begin 
      return (Core.tuple)($(Expr(:new, Pair{Int64,Int64}, :((Base.getfield)(t, 1)::Int64), :((Base.getfield)(u, 1)::Int64))), $(Expr(:new, Pair{Int64,Int64}, :((Core.getfield)(t, 2)::Int64), :((Core.getfield)(u, 2)::Int64))))::Tuple{Pair{Int64,Int64},Pair{Int64,Int64}}
  end::Tuple{Pair{Int64,Int64},Pair{Int64,Int64}}

julia> @code_warntype pairzip((1, :x, r"x"), ("w", 1.0, +))
Variables:
  #self#::#pairzip
  t::Tuple{Int64,Symbol,Regex}
  u::Tuple{String,Float64,Base.#+}

Body:
  begin 
      SSAValue(1) = (Core.getfield)(t::Tuple{Int64,Symbol,Regex}, 2)::Symbol
      SSAValue(2) = (Core.getfield)(t::Tuple{Int64,Symbol,Regex}, 3)::Regex
      return (Core.tuple)($(Expr(:new, Pair{Int64,String}, :((Base.getfield)(t, 1)::Int64), :((Base.getfield)(u, 1)::String))), $(Expr(:new, Pair{Symbol,Float64}, SSAValue(1), :((Core.getfield)(u, 2)::Float64))), $(Expr(:new, Pair{Regex,Base.#+}, SSAValue(2), :($(QuoteNode(+))))))::Tuple{Pair{Int64,String},Pair{Symbol,Float64},Pair{Regex,Base.#+}}
  end::Tuple{Pair{Int64,String},Pair{Symbol,Float64},Pair{Regex,Base.#+}}

```

Here’s my solution, using 3 methods.

---

<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:** [April 26, 2017, 7:40pm UTC](https://discourse.julialang.org/t/type-stable-zip-to-pairs/3390/3 "2017-04-26T19:40:37Z")

</div>

Yes!! This is perfect, thank you!!

---

<div class="post-metadata">

**Author:** ![jameson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jameson/32/23_2.png) [@jameson](https://discourse.julialang.org/u/jameson)\
**Post date:** [April 28, 2017, 6:35pm UTC](https://discourse.julialang.org/t/type-stable-zip-to-pairs/3390/4 "2017-04-28T18:35:37Z")

</div>

You can also use the `ntuple` function to push the complexity of getting the right algorithm into a lower layer (fengyang.wang’s solution is O(N^2), while ntuple is O(N), like your original code)

```julia
function pairzip(x::Tuple{Vararg{Any, N}}, y::Tuple{Vararg{Any, N}}) where {N}
    return ntuple(i -> (x[i] => y[i]), Val{N})
end

```

---

<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:** [April 28, 2017, 6:41pm UTC](https://discourse.julialang.org/t/type-stable-zip-to-pairs/3390/5 "2017-04-28T18:41:49Z")

</div>

@jameson that was the first thing I tried, but it seems not to be type-stable for heterogeneous tuples (presumably since the anonymous function’s output isn’t a consistent type?)

```julia
julia> function pairzip(x::Tuple{Vararg{Any, N}}, y::Tuple{Vararg{Any, N}}) where {N}
           return ntuple(i -> (x[i] => y[i]), Val{N})
       end
pairzip (generic function with 1 method)

julia> pairzip((1, 1.0), ("x", :y))
(1=>"x", 1.0=>:y)

julia> @code_warntype pairzip((1, 1.0), ("x", :y))
Variables:
  #self#::#pairzip
  x::Tuple{Int64,Float64}
  y::Tuple{String,Symbol}
  #1::##1#2{Tuple{Int64,Float64},Tuple{String,Symbol}}

Body:
  begin 
      #1::##1#2{Tuple{Int64,Float64},Tuple{String,Symbol}} = $(Expr(:new, ##1#2{Tuple{Int64,Float64},Tuple{String,Symbol}}, :(x), :(y)))
      $(Expr(:inbounds, false))
      # meta: location tuple.jl ntuple 128
      # meta: location tuple.jl _ntuple 135
      SSAValue(4) = $(Expr(:invoke, MethodInstance for (::##1#2{Tuple{Int64,Float64},Tuple{String,Symbol}})(::Int64), :(#1), :((Base.add_int)(0, 1)::Int64)))
      # meta: location tuple.jl _ntuple 135
      SSAValue(3) = (Core.tuple)(SSAValue(4), $(Expr(:invoke, MethodInstance for (::##1#2{Tuple{Int64,Float64},Tuple{String,Symbol}})(::Int64), :(#1), :((Base.add_int)(1, 1)::Int64))))::Tuple{Any,Any}
      # meta: pop location
      # meta: pop location
      # meta: pop location
      $(Expr(:inbounds, :pop))
      return SSAValue(3)
  end::Tuple{Any,Any}

```
