Print an N-dimensional array

This does not work. I used the following functions:

func cumulativeProdTwo(_ array: [Int]) -> [Int] {
    array.reduce(into: [Int]()) { partialResult, x in
        if let last = partialResult.last {
            partialResult.append(last * x + x - 1)
        } else {
            partialResult.append(x)
        }
    }
}

func descriptionTwo<T>(for array: [T], with shape: [Int]) -> String {
    if shape.count == 1 {
        var descr = "( "
        descr += array.map { "\($0)" }.joined(separator: "  ")
        descr += " )"
        return descr
    }

    let chunkSize = shape.last ?? 1

    let chunks = stride(from: 0, to: numbers.count, by: chunkSize).map {
        Array(numbers[$0..<min($0 + chunkSize, numbers.count)])
    }

    let width = array.map { "\($0)".count }.max() ?? 1

    let lines = chunks.map { row in
        row.map { element in
            (String(repeating: " ", count: width) + element.description).suffix(width)
        }.joined(separator: "  ")
    }

    var r = shape
    r.removeLast()
    r.reverse()
    let rows = cumulativeProdTwo(r)

    var n = 0
    let last = rows.last!

    var descr = ""

    func add(line: String) {
        for row in rows.reversed() {
            switch n % (row + 1) {
            case 0:
                descr += "⎛ "
            case row - 1:
                descr += "⎝ "
            case row:
                descr += "  "
            default:
                descr += "⎜ "
            }
        }

        descr += line

        for row in rows {
            switch n % (row + 1) {
            case 0:
                descr += " ⎞"
            case row - 1:
                descr += " ⎠"
            case row:
                descr += "  "
            default:
                descr += " ⎟"
            }
        }

        descr += "\n"
        n += 1
    }

    for line in lines {
        if n % (last + 1) == last {
            add(line: String(repeating: " ", count: (width + 2) * chunkSize - 2))
        }

        add(line: line)
    }

    return descr
}

And I get the following result:

let shape = [2, 3, 2, 4]
let numbers = Array(1...shape.reduce(1, *))

let d2 = descriptionTwo(for: numbers, with: shape)
print(d2)
⎛ ⎛ ⎛  1   2   3   4 ⎞ ⎞ ⎞
⎜ ⎜ ⎝  5   6   7   8 ⎠ ⎟ ⎟
⎜ ⎜    9  10  11  12   ⎟ ⎟
⎜ ⎜ ⎛ 13  14  15  16 ⎞ ⎟ ⎟
⎜ ⎜ ⎝ 17  18  19  20 ⎠ ⎟ ⎟
⎜ ⎜   21  22  23  24   ⎟ ⎟
⎜ ⎜ ⎛ 25  26  27  28 ⎞ ⎟ ⎟
⎜ ⎝ ⎝ 29  30  31  32 ⎠ ⎠ ⎟
⎜     33  34  35  36     ⎟
⎜ ⎛ ⎛ 37  38  39  40 ⎞ ⎞ ⎟
⎜ ⎜ ⎝ 41  42  43  44 ⎠ ⎟ ⎟
⎜ ⎜   45  46  47  48   ⎟ ⎟

Replace rows.last! with rows.first!.

Yes

Use a WebView or get a browser to load it from an html file.

x_eq_x

Details
<!doctype html>
<html>

<header>
<script id='MathJax-script' async src='https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'></script>
</header>

<body>
   <math xmlns='http://www.w3.org/1998/Math/MathML' display='inline'>
   <mrow>
      <mi mathsize='2.5em' mathcolor='#00AAFF'>x</mi>
      <mo mathsize='2.5em' mathcolor='#00AAFF'>&equals;</mo>
      <mi mathsize='2.5em' mathcolor='#00AAFF'>x</mi>
   </mrow>
   </math>
</body>

</html>

No, but I would use a method similar to yours.

Can you explain your changes to this function? It looks like the addition of x - 1 accounts for the extra blank lines in the printed array.

func cumulativeProd(_ array: [Int]) -> [Int] {
    array.reduce(into: [Int]()) { partialResult, x in
        if let last = partialResult.last {
            partialResult.append(last * x + x - 1)
        } else {
            partialResult.append(x)
        }
    }
}

But this is no longer a cumulative product such as
[a, b, c] -> [a, b * a, c * b]
it's more like
[a, b*a + (b - 1), c*b + (c - 1)]
so maybe the function should be called something like calculateLastRows().

Yes, that's its purpose. You can call it calculateHeights if you prefer. 2, 8 and 17 are the respective heights of the submatrices placed on top.

Ok, this is what I have for the two functions:

func getLastRows(_ array: [Int]) -> [Int] {
    array.reduce(into: [Int]()) { partialResult, x in
        if let last = partialResult.last {
            partialResult.append(last * x + x - 1)
        } else {
            partialResult.append(x)
        }
    }
}

func descriptionTwo<T>(for array: [T], with shape: [Int]) -> String {
    if shape.count == 1 || shape.count == 2 && shape[0] == 1{
        var descr = "( "
        descr += array.map { "\($0)" }.joined(separator: "  ")
        descr += " )"
        return descr
    }

    let chunkSize = shape.last ?? 1

    let chunks = stride(from: 0, to: numbers.count, by: chunkSize).map {
        Array(numbers[$0..<min($0 + chunkSize, numbers.count)])
    }

    let width = array.map { "\($0)".count }.max() ?? 1

    let lines = chunks.map { row in
        row.map { element in
            (String(repeating: " ", count: width) + element.description).suffix(width)
        }.joined(separator: "  ")
    }

    var r = shape
    r.removeLast()
    r.reverse()
    let rows = getLastRows(r) // is [2, 8, 17]

    var n = 0
    let last = rows.first ?? 1

    var descr = ""

    func add(line: String) {
        for row in rows.reversed() {
            switch n % (row + 1) {
            case 0:
                descr += "⎛ "
            case row - 1:
                descr += "⎝ "
            case row:
                descr += "  "
            default:
                descr += "⎜ "
            }
        }

        descr += line

        for row in rows {
            switch n % (row + 1) {
            case 0:
                descr += " ⎞"
            case row - 1:
                descr += " ⎠"
            case row:
                descr += "  "
            default:
                descr += " ⎟"
            }
        }

        if n != rows.last! - 1 {
            descr += "\n"
        }

        n += 1
    }

    for line in lines {
        if n % (last + 1) == last {
            add(line: String(repeating: " ", count: (width + 2) * chunkSize - 2))
        }

        add(line: line)
    }

    return descr
}

Which gives the following result:

let shape = [2, 3, 2, 4]
let numbers = Array(1...shape.reduce(1, *)).map { Float($0) }

let d2 = descriptionTwo(for: numbers, with: shape)
print(d2)
⎛ ⎛ ⎛  1.0   2.0   3.0   4.0 ⎞ ⎞ ⎞
⎜ ⎜ ⎝  5.0   6.0   7.0   8.0 ⎠ ⎟ ⎟
⎜ ⎜                            ⎟ ⎟
⎜ ⎜ ⎛  9.0  10.0  11.0  12.0 ⎞ ⎟ ⎟
⎜ ⎜ ⎝ 13.0  14.0  15.0  16.0 ⎠ ⎟ ⎟
⎜ ⎜                            ⎟ ⎟
⎜ ⎜ ⎛ 17.0  18.0  19.0  20.0 ⎞ ⎟ ⎟
⎜ ⎝ ⎝ 21.0  22.0  23.0  24.0 ⎠ ⎠ ⎟
⎜                                ⎟
⎜ ⎛ ⎛ 25.0  26.0  27.0  28.0 ⎞ ⎞ ⎟
⎜ ⎜ ⎝ 29.0  30.0  31.0  32.0 ⎠ ⎟ ⎟
⎜ ⎜                            ⎟ ⎟
⎜ ⎜ ⎛ 33.0  34.0  35.0  36.0 ⎞ ⎟ ⎟
⎜ ⎜ ⎝ 37.0  38.0  39.0  40.0 ⎠ ⎟ ⎟
⎜ ⎜                            ⎟ ⎟
⎜ ⎜ ⎛ 41.0  42.0  43.0  44.0 ⎞ ⎟ ⎟
⎝ ⎝ ⎝ 45.0  46.0  47.0  48.0 ⎠ ⎠ ⎠

This works great except for some edge cases like:

let shape = [2, 3, 1, 4]
let numbers = Array(1...shape.reduce(1, *)).map { Float($0) }

let d2 = descriptionTwo(for: numbers, with: shape)
print(d2)
⎛ ⎛ ⎛  1.0   2.0   3.0   4.0 ⎞ ⎞ ⎞
⎜ ⎜                            ⎟ ⎟
⎜ ⎜ ⎛  5.0   6.0   7.0   8.0 ⎞ ⎟ ⎟
⎜ ⎜                            ⎟ ⎟
⎜ ⎝ ⎛  9.0  10.0  11.0  12.0 ⎞ ⎠ ⎟
⎜                                ⎟
⎜ ⎛ ⎛ 13.0  14.0  15.0  16.0 ⎞ ⎞ ⎟
⎜ ⎜                            ⎟ ⎟
⎜ ⎜ ⎛ 17.0  18.0  19.0  20.0 ⎞ ⎟ ⎟
⎜ ⎜                            ⎟ ⎟
⎝ ⎝ ⎛ 21.0  22.0  23.0  24.0 ⎞ ⎠ ⎠

This handles the case where the base matrix is just one row:

func descriptionTwo<T>(for array: [T], with shape: [Int]) -> String {
    if shape.count == 1 || shape.count == 2 && shape[0] == 1 {
        var descr = "( "
        descr += array.map { "\($0)" }.joined(separator: "  ")
        descr += " )"
        return descr
    }

    let chunkSize = shape.last ?? 1

    let chunks = stride(from: 0, to: array.count, by: chunkSize).map {
        Array(array[$0..<min($0 + chunkSize, array.count)])
    }

    let width = array.map { "\($0)".count }.max() ?? 1

    let lines = chunks.map { row in
        row.map { element in
            (String(repeating: " ", count: width) + "\(element)").suffix(width)
        }.joined(separator: "  ")
    }

    var r = shape
    r.removeLast()
    r.reverse()
    let rows = getLastRows(r) // is [2, 8, 17]

    var n = 0
    let last = rows.first ?? 1

    var descr = ""

    func add(line: String) {
        for row in rows.reversed() {
            switch n % (row + 1) {
            case 0:
                if row == 1 { descr += "( " } else { descr += "⎛ " }
            case row - 1:
                descr += "⎝ "
            case row:
                descr += "  "
            default:
                descr += "⎜ "
            }
        }

        descr += line

        for row in rows {
            switch n % (row + 1) {
            case 0:
                if row == 1 { descr += " )" } else { descr += " ⎞" }
            case row - 1:
                descr += " ⎠"
            case row:
                descr += "  "
            default:
                descr += " ⎟"
            }
        }

        if n != rows.last! - 1 {
            descr += "\n"
        }

        n += 1
    }

    for line in lines {
        if n % (last + 1) == last {
            add(line: String(repeating: " ", count: (width + 2) * chunkSize - 2))
        }

        add(line: line)
    }

    return descr
}