# Array flattening function

**URL:** <https://discourse.julialang.org/t/array-flattening-function/122154>\
**Category:** General Usage\
**Tags:** arrays\
**Created:** [November 1, 2024, 8:32pm UTC](https://discourse.julialang.org/t/array-flattening-function/122154 "2024-11-01T20:32:53Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [November 1, 2024, 8:32pm UTC](https://discourse.julialang.org/t/array-flattening-function/122154/1 "2024-11-01T20:32:53Z")

</div>

I’m looking for the function that takes a `(m,n)` matrix of `(k,l)` matrices and makes a `(m*k, n*l)` matrix? And likewise for other dimensions. This works for 2d but it’s a little verbose. Is there a simpler and more general way to write this?

```julia
    b = Matrix{Int}.(permutedims(reshape(Base.vect(
        zeros(3,4), ones(3,2), 2ones(3,5),
        3ones(1, 4), 4ones(1, 2), 5ones(1, 5),
    ), 3, 2), (2,1)))
    @test reduce(hcat, map(splat(vcat), eachslice(b; dims=2))) ==
        [
            0 0 0 0 1 1 2 2 2 2 2
            0 0 0 0 1 1 2 2 2 2 2
            0 0 0 0 1 1 2 2 2 2 2
            3 3 3 3 4 4 5 5 5 5 5
        ]

```

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [November 1, 2024, 8:55pm UTC](https://discourse.julialang.org/t/array-flattening-function/122154/2 "2024-11-01T20:55:05Z")

</div>

Perhaps you’re looking for `hvcat`? It takes arguments in row-major order, like matrix syntax (`[a b; c d]`), so to flatten an existing block matrix you need to apply `permutedims` before splatting:

```julia-repl
julia> hvcat(3, permutedims(b)...)
4×11 Matrix{Int64}:
 0 0 0 0 1 1 2 2 2 2 2
 0 0 0 0 1 1 2 2 2 2 2
 0 0 0 0 1 1 2 2 2 2 2
 3 3 3 3 4 4 5 5 5 5 5

```

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [November 1, 2024, 9:38pm UTC](https://discourse.julialang.org/t/array-flattening-function/122154/3 "2024-11-01T21:38:10Z")

</div>

That works on `Matrix` but seems wrong on `Vector`. The function I’m looking for has `f(Matrix{Matrix{T}})::Matrix{T}` and `f(Vector{Vector{T}})::Vector{T}`.

`f([[1,2], [3,4]]) == [1,2,3,4]`.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [November 2, 2024, 7:15am UTC](https://discourse.julialang.org/t/array-flattening-function/122154/4 "2024-11-02T07:15:07Z")

</div>

is this close to what you want?

```julia
f(b::Matrix{Matrix{T}}) where T = hvcat(3, permutedims(b)...)
f(b::Vector{Vector{T}}) where T = vcat(b...)

```

```julia
using BlockArrays

Bb=mortar(b)

Matrix(Bb)

c=[[1,2], [3,4]]
Bc=mortar(c)
Vector(Bc)

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [November 2, 2024, 2:45pm UTC](https://discourse.julialang.org/t/array-flattening-function/122154/5 "2024-11-02T14:45:29Z")

</div>

This works for both matrix `b` and vector `c`:

```julia
reduce(hcat, reduce(vcat, u) for u in eachcol(b)) 

```

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [November 2, 2024, 3:23pm UTC](https://discourse.julialang.org/t/array-flattening-function/122154/6 "2024-11-02T15:23:44Z")

</div>

I think the general case can be written with `hvncat`.

It’s possible that Base ought to have such a function… it could replace & generalise `reduce(vcat, A)`, similar to how `stack` replaces/generalises `reduce(hcat, A)`.

I wrote a version of this function [here](https://github.com/mcabbott/LazyStack.jl/blob/master/src/concatenate.jl#L321). It obeys these rules but should be faster than `hvcat` & `hvncat`:

```julia
concatenate(A::AbstractVector{<:AbstractVecOrMat}) = reduce(vcat, A)
concatenate(A::AbstractMatrix{<:AbstractVecOrMat}) = hvcat(size(A,2), permutedims(A)...)
concatenate(A::AbstractArray{<:AbstractArray}) = hvncat(size(A), false, A...)

concatenate([[1,2], [3,4,5], [6]]) == 1:6 # vector of vectors -> 6-element Vector

mats = [fill(10i+j, i, j) for i in 1:2, j in 3:5];
concatenate(mats) # 3×12 block matrix

concatenate([rand(2,2,2,2) for _ in 1:3, _ in 1:4, _ in 1:5]) |> size # 6×8×10×2 Array

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [November 2, 2024, 3:37pm UTC](https://discourse.julialang.org/t/array-flattening-function/122154/7 "2024-11-02T15:37:47Z")

</div>

@mcabbott, what am I doing wrong below, as I observe these performance figures for OP’s matrix `b`:

```julia
@btime concatenate($b) # 801 ns (12 allocs: 880 bytes)

@btime reduce(hcat, reduce(vcat, u) for u in eachcol($b)) # 177 ns (10 allocs: 1.28 KiB)

```

_EDIT:_  
_Error was that code above was not using LazyStack.concatenate()_

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [November 2, 2024, 3:51pm UTC](https://discourse.julialang.org/t/array-flattening-function/122154/8 "2024-11-02T15:51:11Z")

</div>

Usually `hvcat` / `hvncat` are fairly slow, they exist only to parse literal `[a;;;b;;;c]` input.

Note BTW that `reduce(hcat, reduce(vcat, u) for u in eachcol(b))` misses the fast method `reduce(hcat, xs)`, so it applies `hcat` pairwise. The fact that this is such an easy mistake to make is why we should not rely on these magic fast paths, and have explicit functions.

Timing almost all of them, on a slightly bigger example:

```julia
julia> conc_jar(b) = reduce(hcat, map(splat(vcat), eachslice(b; dims=2))); # question

julia> conc_RG(b) = reduce(hcat, reduce(vcat, u) for u in eachcol(b)); # rafael.guerra

julia> conc_RG2(b) = reduce(hcat, [reduce(vcat, u) for u in eachcol(b)]); # avoiding pairwise hcat

julia> conc_hvcat(A::AbstractMatrix{<:AbstractVecOrMat}) = hvcat(size(A,2), permutedims(A)...);

julia> import LazyStack, BlockArrays

julia> let A = [rand(10, 10) for _ in 1:10, _ in 1:10]
         @btime conc_jar($A)
         @btime conc_hvcat($A)
         @btime Matrix(mortar($A))
         println("RG:")
         @btime conc_RG($A)
         @btime conc_RG2($A)
         println("MA:")
         @btime LazyStack.concatenate($A)
       end;
  23.708 μs (283 allocations: 166.91 KiB)
  15.250 μs (13 allocations: 81.22 KiB)
  11.958 μs (13 allocations: 80.41 KiB)
RG:
  28.500 μs (57 allocations: 502.36 KiB)
  9.583 μs (35 allocations: 157.88 KiB)
MA:
  7.500 μs (3 allocations: 78.20 KiB)

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [November 2, 2024, 3:54pm UTC](https://discourse.julialang.org/t/array-flattening-function/122154/9 "2024-11-02T15:54:20Z")

</div>

Fortunately there are stars in the Julia sky to light the candle for us.
