Elegant way to turn a Array{T,N} to a Array{Array{T,M},N-M}

Viewed 69

Let's say I'm starting with:

x = collect(reshape(1:24, (3,4,2)))
3×4×2 Array{Int64,3}:
[:, :, 1] =
 1  4  7  10
 2  5  8  11
 3  6  9  12

[:, :, 2] =
 13  16  19  22
 14  17  20  23
 15  18  21  24

I want to get to this:

3×2 Array{Array{Int64,1},2}:
 [1, 4, 7, 10]  [13, 16, 19, 22]
 [2, 5, 8, 11]  [14, 17, 20, 23]
 [3, 6, 9, 12]  [15, 18, 21, 24]

It's sort of what eachslice does, except I need to iterate across more than 1 dimension. So far, I can accomplish this for this particular array with broadcasting getindex and very awkward reshaping of axes:

y = getindex.(Ref(x), axes(x,1), Ref(:),
                      reshape(axes(x,3), 1, length(axes(x,3)) )
              )

But it's obviously very cumbersome to adjust for different array shapes. The main hangup is having to reshape the axes for broadcasting, it's not as clean as getindex(x,:,1,:) where the axes are presumed orthogonal.

1 Answers

In addition to the solutions already discussed this can be achieved using mapslices:

julia> mapslices(a -> [a], x; dims=(2,))
3×1×2 Array{Vector{Int64}, 3}:
[:, :, 1] =
 [1, 4, 7, 10]
 [2, 5, 8, 11]
 [3, 6, 9, 12]

[:, :, 2] =
 [13, 16, 19, 22]
 [14, 17, 20, 23]
 [15, 18, 21, 24]

the result is in this case an 3x1x2 Array but we can easily create a function that also drops the singleton dimension:

reslice(a, dims) = dropdims(mapslices(x -> [x], x; dims); dims)
julia> reslice(x, (2,))
3×2 Matrix{Vector{Int64}}:
 [1, 4, 7, 10]  [13, 16, 19, 22]
 [2, 5, 8, 11]  [14, 17, 20, 23]
 [3, 6, 9, 12]  [15, 18, 21, 24]

This solution is not as efficient as the "getindex" solution but may better capture the intention.

Related