De-structure results from broadcasting

Hello!

When not using broadcasting, there is a way to destructure results from a function:

julia> f(x) = (x, x^2);

julia> (y1, y2) = f(5);

julia> y1
5

julia> y2
25

However, this does not work while broadcasting:

julia> x = 1:10
1:10

julia> (z1, z2) = f.(x);

julia> z1
(1, 1)

julia> z2
(2, 4)

Is there any way to do the same, and have in z1 and z2 arrays with the collection of the first and second arguments of the result respectively?

Sadly, no. The feature you want is unzip. There is a PR here which has not gotten merged into Base.

There is a package for this, though: Unzip.jl.

1 Like

notice this produces:

julia> f.(1:10)
10-element Vector{Tuple{Int64, Int64}}:
 (1, 1)
 (2, 4)
 (3, 9)
 (4, 16)
 (5, 25)
 (6, 36)
 (7, 49)
 (8, 64)
 (9, 81)
 (10, 100)

which is not ideal storage in memory to begin with if you’re interested in the “columns” (in this display). you can do:

julia> [x^y for x in 1:10, y in 1:2]
10×2 Matrix{Int64}:
  1    1
  2    4
  3    9
  4   16
  5   25
  6   36
  7   49
  8   64
  9   81
 10  100

Sadly, there’s no syntax for multi dimensional comprehension (probably would be too confusing)

You can also do this with StructArrays.jl:

julia> using StructArrays

julia> using StructArrays: components

julia> z1, z2 = components(StructArray(f.(1:10)))
([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 4, 9, 16, 25, 36, 49, 64, 81, 100])

edit: this naturally suggests

unzip(f, x) = components(StructArray(f(xᵢ...) for xᵢ in x))

which works well for multidimensional iterators:

julia> g(x, y) = x*y, y/x
g (generic function with 1 method)

julia> v1, v2 = unzip(g, Iterators.product(1:3, 1:4))
([1 2 3 4; 2 4 6 8; 3 6 9 12], [1.0 2.0 3.0 4.0; 0.5 1.0 1.5 2.0; 0.3333333333333333 0.6666666666666666 1.0 1.3333333333333333])

julia> v1
3×4 Matrix{Int64}:
 1  2  3   4
 2  4  6   8
 3  6  9  12
1 Like

There is also Destruct.jl:

using Destruct
f(x) = (x, x^2)
x = 1:10
z1, z2 = f.(x) |> destruct

This yields

z1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
z2 = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]