Convert tuple to array

question

#1

Is there a way to quickly convert a tuple to an array?

A common use case is when I want to perform calculations on size(my_array), say dividing the size by half or other more complex array operations.

Currently I expand the tuple like dx, dy, dz = size(my_array); dims = [dx, dy, dz] but that gets tedious after doing it 100+ times. I would much rather have size(my_array) return an array, or at the very least have a lazy way to convert it to a array.


#2

I guess you could do map(x -> x ÷ 2, size(my_array))


#3

Sorry, I should have made it more clear. I gave division by two as an example, but I have more complicated array operations in mind. Thus I need the dimensions in an array.


#4

If it is the same operation on every element you still can easily use map. Just define a function that does the operation you want and map it over the tuple.

If you really need an array you can do [i for i in tuple] to create an array from the elements in the tuple.


#5

The latter is what I was looking for. Completely overlooked comprehensions. Thank you!


#6

You could also use collect(tuple)


#7

Ah, this is prefect! I haven’t seen collect used outside of ranges before.

I timed it for small tuples and this is much faster than a list comprehension:

julia> @time(collect((1,2,3))) 0.000006 seconds (7 allocations: 320 bytes) 3-element Array{Int64,1}: 1 2 3 julia> @time([i for i in (1,2,3)]) 0.018129 seconds (13.05 k allocations: 584.068 KB) 3-element Array{Int64,1}: 1 2 3


#8

This timing is not accurate. Always time in a function and run more than once.

julia> f(tup) = @time collect(tup)
f (generic function with 1 method)

julia> f((1, 2, 3))
  0.000001 seconds (1 allocation: 112 bytes)
3-element Array{Int64,1}:
 1
 2
 3

julia> f((1, 2, 3))
  0.000000 seconds (1 allocation: 112 bytes)
3-element Array{Int64,1}:
 1
 2
 3

julia> g(tup) = @time [i for i in tup]
g (generic function with 1 method)

julia> g((1, 2, 3))
  0.000001 seconds (1 allocation: 112 bytes)
3-element Array{Int64,1}:
 1
 2
 3

julia> g((1, 2, 3))
  0.000000 seconds (1 allocation: 112 bytes)
3-element Array{Int64,1}:
 1
 2
 3

Hence they are both equally efficient.


#9

Both should perform the same. Do not benchmark in global scope. If you put them in a function, e.g. foo(t) = collect(t) and bar(t) = [i for i in t], you should see about the same performance.


#10

My mistake! I tested it via functions and as you say they are about the same. I am coming back to Julia after ~1 year apart and am a bit rusty.


#11
julia> a = (2,3,4)
(2,3,4)

julia> b = [2,3,4]
3-element Array{Int64,1}:
 2
 3
 4

julia> [a...]
3-element Array{Int64,1}:
 2
 3
 4

julia> (b...)
(2,3,4)

#12

Ooh, is this the fathomed ‘splat’ I have been reading about? Didn’t think it could be used this way. Very concise!


#13
julia> using BenchmarkTools

julia> a = (1, 2, 3)
(1,2,3)

julia> a_large = ([rand(1:5) for i in 1:50]...)
(2,1,2,1,2,1,3,5,1,1,5,4,1,5,3,4,2,2,3,3,2,3,5,1,4,4,5,2,2,3,1,2,5,2,1,2,3,2,2,4,1,3,1,4,2,3,5,2,4,1)

julia> f_splat(a)   = [a...];

julia> f_collect(a) = collect(a);

julia> f_comp(a)    = [i for i in a];

julia> @btime f_splat($a);
  35.741 ns (1 allocation: 112 bytes)

julia> @btime f_collect($a);
  31.897 ns (2 allocations: 128 bytes)

julia> @btime f_comp($a);
  26.184 ns (1 allocation: 112 bytes)

julia> @btime f_splat($a_large);
  774.571 ns (2 allocations: 992 bytes)

julia> @btime f_collect($a_large);
  82.332 ns (2 allocations: 560 bytes)

julia> @btime f_comp($a_large);
  88.702 ns (1 allocation: 544 bytes)

#14

Nice! Seems best to avoid splats for large tuples, but otherwise any choice is roughly equally performant. As a bonus, BenchmarkTools will be very handy to me.

Maybe mention of these methods and their relative performance should go in the official docs?