Unpack array of tuples and structs

Hello everyone,

I have a question which seems to be kind of related to Convert array of tuples to vector.

Edit : I just realized I can do these things with StructArrays except for the tuple case.
However, I wonder if there is different way this can be done.

Let me give a MWE.

julia> struct Input
             a
             b
       end

julia> struct Output
             a
             b
       end

julia> function f1(inp :: Input, x)
           return  x*inp.a, x*inp.b
       end 
f1 (generic function with 1 method)

julia> function f2(inp :: Input, x)
           return OutPut(x*inp.a, x*inp.b)
       end

julia> inp = Input(2.0,3.0)
Input(2.0, 3.0)

julia> f1(inp, 10)
(20.0, 30.0)

julia> f2(inp, 10)
OutPut(20.0, 30.0)

julia> v = 1:5
1:5

Now, if I input vector v to f1 I get

c =f1.(Ref(inp), v)
10-element Array{Tuple{Float64,Float64},1}:
 (2.0, 3.0)
 (4.0, 6.0)
 (6.0, 9.0)
 (8.0, 12.0)
 (10.0, 15.0)

Question 1:
How can I get the the first, or second element of each tuple into a vector?

julia> c[:][1]
(2.0, 3.0)

which is not what I want.
StructArrays does not seem to help here.It gives the same result.

Now, I can do the following:

julia> get_a(op :: Output) = op.a
get_a (generic function with 1 method)

julia> d =f2.(Ref(inp), v)
10-element Array{Output,1}:
 Output(2.0, 3.0)
 Output(4.0, 6.0)
 Output(6.0, 9.0)
 Output(8.0, 12.0)
 Output(10.0, 15.0)

julia> get_a.(d)
10-element Array{Float64,1}:
  2.0
  4.0
  6.0
  8.0
 10.0

This works great.
I was wondering if there was another way to achieve something similar.
I hoped that @unpack from Parameters.jl could do something like

julia> @unpack a, b .= d
ERROR: LoadError: Expression needs to be of form `a, b = c`
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] @unpack(::LineNumberNode, ::Module, ::Any) at /home/omerchiers/.julia/packages/Parameters/l76EM/src/Parameters.jl:743
in expression starting at none:1

but unfortunately it doesn’t work.

Question 2:
Again, with StructArrays I can achieve my goal.
But still I was wondering if there is some way to use @unpack like that?

Thanks in advance,
Olivier

2 Likes

I love comprehensions (did I say that already?):

[ x[1] for x in c ]
5-element Array{Float64,1}:
  2.0
  4.0
  6.0
  8.0
 10.0
2 Likes

that’s great!
thanks a lot

1 Like

Arguably the more idiomatic solution here is first.(c) and last.(c), more generally you can broadcast getindex like getindex.(c, 1)

2 Likes

EDIT: I totally missed the point. I delete the post.

Thanks for the suggestion.

The Input struct was probably not really necessary in my MWE.
I just wanted to show two functions: one that outputs a tuple and another one that outputs some struct.
And how to handle these two cases when outputting a vector of those.

But thanks anyway.

ooh that’s also very cool.
but now this makes me wonder why the following things work as expected

  • getindex.(c,1)
  • c[1][1]
  • c[:]

but this

  • c[:][1]

does not

That’s equivalent to:

temp = c[:]
temp[1]

Try running those two lines and look at what temp ends up as.

In other words, the expression c[:][1] creates a copy of c and then takes the first element of that copy. It’s a valid thing to do, but not a very useful one.

2 Likes

yes indeed, now it makes sense.

julia> c == c[:]
true

hence

julia> c[1] == c[:][1]
true

Totally logical, but somehow I find it disturbing.
Thanks a lot for this very insightful example.