# Print an N-dimensional array

**URL:** <https://forums.swift.org/t/print-an-n-dimensional-array/74082>\
**Category:** Using Swift\
**Tags:** string, array\
**Created:** [August 21, 2024, 5:03am UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082 "2024-08-21T05:03:07Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [August 21, 2024, 5:03am UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/1 "2024-08-21T05:03:07Z")

</div>

I can print a 2D array by providing the shape of a flat array as shown below:

```swift
let numbers: [Float] = [1, 2, 3, 4, 5, 6]
let shape = [2, 3]

var result = ""

for i in 0..<shape[0] {
    for j in 0..<shape[1] {
        let idx = flatIndex(indices: [i, j], shape: shape)
        result += "\(numbers[idx]) "
    }
    result += "\n"
}

print(result)

```

```plaintext
1.0 2.0 3.0  
4.0 5.0 6.0  

```

The flat index is calculated with this function:

```swift
func flatIndex(indices: [Int], shape: [Int]) -> Int {
    var index = 0
    var stride = 1

    for i in (0..<shape.count).reversed() {
        index += indices[i] * stride
        stride *= shape[i]
    }

    return index
}

```

Similarly, I can print a 3D array as shown here:

```swift
let numbers: [Float] = [1, 2, 3,
                        4, 5, 6,

                        7, 8, 9,
                        10, 11, 12,

                        13, 14, 15,
                        16, 17, 18]

let shape = [3, 2, 3]

var result = ""

for k in 0..<shape[0] {
    for i in 0..<shape[1] {
        for j in 0..<shape[2] {
            let idx = flatIndex(indices: [k, i, j], shape: shape)
            result += "\(numbers[idx]) "
        }
        result += "\n"
    }
    result += "\n"
}

print(result)

```

```plaintext
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  

```

How can I generalize this to print any number of dimensions (N-dimensions)?

---

<div class="post-metadata">

**Author:** ![xAlien95](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xalien95/32/12558_2.png) [@xAlien95](https://forums.swift.org/u/xAlien95)\
**Post date:** [August 21, 2024, 8:06am UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/2 "2024-08-21T08:06:46Z")

</div>

MATLAB's approach is to only print the first two dimensions as matrices and repeat for every extra-dimension index:

```swift
>> rand(3, 2, 2, 3)

ans(:,:,1,1) =

    0.8143 0.3500
    0.2435 0.1966
    0.9293 0.2511

ans(:,:,2,1) =

    0.6160 0.8308
    0.4733 0.5853
    0.3517 0.5497

ans(:,:,1,2) =

    0.9172 0.7537
    0.2858 0.3804
    0.7572 0.5678

ans(:,:,2,2) =

    0.0759 0.7792
    0.0540 0.9340
    0.5308 0.1299

ans(:,:,1,3) =

    0.5688 0.3371
    0.4694 0.1622
    0.0119 0.7943

ans(:,:,2,3) =

    0.3112 0.6020
    0.5285 0.2630
    0.1656 0.6541

```

---

<div class="post-metadata">

**Author:** ![vns](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/vns/32/37396_2.png) [@vns](https://forums.swift.org/u/vns)\
**Post date:** [August 21, 2024, 8:19am UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/3 "2024-08-21T08:19:12Z")

</div>

> [@wigging](#):
>
> How can I generalize this to print any number of dimensions (N-dimensions)?

You can recursively collect shape iterators with function like

```swift
func print(
    iteration: Int,
    shapeIterators: [Int], 
    in array: [T],
    of shape: [Int], 
    result: inout String
) {
    for k in shape[iteration] {
        if iteration != shape.count - 1 {
            print(
                iteration: iteration + 1, 
                shapeIterators: shapeIterators + [k], 
                in: array,
                of: shape, 
                result: &result
            )
        } else { /* ... */ }
    }
}

```

Not pretty, but I hope idea is clear. I suppose recursive version should be good enough, if you don't expect large shapes, otherwise I'd modified it into a loop-based.

---

<div class="post-metadata">

**Author:** ![ibex](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ibex/32/30439_2.png) [@ibex](https://forums.swift.org/u/ibex)\
**Post date:** [August 21, 2024, 12:17pm UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/4 "2024-08-21T12:17:12Z")

</div>

> [@wigging](#):
>
> How can I generalize this to print any number of dimensions (N-dimensions)?

Divide and conquer: Think of an N-dimensional array as an array (N-1) dimensional arrays.

From one of my toy languages written in Swift.

```swift
v3D := int [2][3][5] = {
         {{1, 2, 3, 0, 1},
          {4, 5, 6, 1, 2},
          {7, 8, 9, 3, 4}},
         {{0, 1, 2, 1, 2},
          {3, 4, 5, 2, 3},
          {6, 7, 8, 0, 1}}
    }

$T << "3D =" << v3D

v4D := int [3][2][3][5] = {
 		{{{1, 0, 3, 0, 1},
          {4, 5, 6, 1, 2},
          {7, 0, 0, 3, 4}},
         {{0, 1, 2, 1, 2},
          {3, 4, 5, 2, 3},
          {6, 7, 8, 0, 1}}},

 		{{{1, 2, 3, 0, 1},
          {0, 5, 0, 1, 2},
          {7, 8, 7, 0, 4}},
         {{0, 0, 2, 1, 2},
          {3, 4, 0, 2, 3},
          {6, 7, 8, 0, 1}}},

	    {{{0, 2, 3, 0, 1},
          {4, 0, 0, 0, 2},
          {7, 8, 9, 3, 4}},
         {{0, 0, 2, 0, 2},
          {3, 4, 3, 2, 3},
          {6, 0, 2, 0, 1}}}
	}

$T << "4D = " << v4D

$T << "4D [0] = " << v4D [0]
$T << "4D [0][0] = " << v4D [0][0]
$T << "4D [0][0][0] = " << v4D [0][0][0]
$T << "4D [0][0][0][0] = " << v4D [0][0][0][0]

```

 ![N-DIM](https://global.discourse-cdn.com/swift/original/3X/0/0/005814d3fe5f718ac6ed4ade835b5365527094e9.jpeg)

---

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [August 21, 2024, 1:22pm UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/5 "2024-08-21T13:22:00Z")

</div>

This is exactly what I'm trying to do but in Swift. I assume you are storing the underlying data as a one-dimensional array. Then how are you taking that 1D array and printing it as multiple dimensions given the shape?

---

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [August 21, 2024, 1:33pm UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/6 "2024-08-21T13:33:04Z")

</div>

So you would start with an empty string like `var description = ""` and pass that string to the `result` parameter like `print(..., result: &description)`?

---

<div class="post-metadata">

**Author:** ![ibex](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ibex/32/30439_2.png) [@ibex](https://forums.swift.org/u/ibex)\
**Post date:** [August 21, 2024, 1:34pm UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/7 "2024-08-21T13:34:07Z")

</div>

> [@wigging](#):
>
> I assume you are storing the underlying data as a one-dimensional array. Then how are you taking that 1D array and printing it as multiple dimensions given the shape?

Yes and by some magical indexing scheme. 🙂

---

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [August 21, 2024, 1:41pm UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/8 "2024-08-21T13:41:24Z")

</div>

I found a [ShapedArray description](https://github.com/swift-numerics-workgroup/swift-shaped-array/blob/8cb542cfc9df0dbfb7e00f2dbd676a4016a40fa4/Sources/ShapedArray/ShapedArrayProtocol.swift#L221) on GitHub but not sure how to apply it to my example.

---

<div class="post-metadata">

**Author:** ![vns](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/vns/32/37396_2.png) [@vns](https://forums.swift.org/u/vns)\
**Post date:** [August 21, 2024, 2:04pm UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/9 "2024-08-21T14:04:53Z")

</div>

Yes, you start with empty string, zero iteration, and empty iterators array. In else branch you then modify string appending flatten row.

> [@wigging](#):
>
> I found a [ShapedArray description](https://github.com/swift-numerics-workgroup/swift-shaped-array/blob/8cb542cfc9df0dbfb7e00f2dbd676a4016a40fa4/Sources/ShapedArray/ShapedArrayProtocol.swift#L221) on GitHub but not sure how to apply it to my example.

That seems like what I’ve suggested btw, in general at least.

* * *

I also have thought about version in which you go in reverse way through shapes and slice array, so it might be less iterations and more optimal, but I’m not sure if that’s a correct algorithm, need to a play a bit with code first.

---

<div class="post-metadata">

**Author:** ![xAlien95](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xalien95/32/12558_2.png) [@xAlien95](https://forums.swift.org/u/xAlien95)\
**Post date:** [August 21, 2024, 6:21pm UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/10 "2024-08-21T18:21:09Z")

</div>

Something like this?

```swift
extension ShapedArray: CustomStringConvertible
where Element: CustomStringConvertible {
  var description: String {
    var shape = shape

    guard let cols = shape.popLast() else { return "()" }

    let width = storage.map(\.description.count).max() ?? 0
    var lines = storage.chunks(ofCount: cols).map { row in
      row.map {
        (String(repeating: " ", count: width) + $0.description)
          .suffix(width)
      }
      .joined(separator: " ")
    }

    var rows = 1
    while let count = shape.popLast() {
      rows *= count

      let brackets = switch rows {
      case 1: [(left: "(", right: ")")].cycled()
      default: (
        [(left: "⎛", right: "⎞")] +
        repeatElement((left: "⎜", right: "⎟"), count: rows - 2) +
        [(left: "⎝", right: "⎠")]
      ).cycled()
      }

      lines = zip(brackets, lines).map { bracket, line in
        [bracket.left, line, bracket.right].joined(separator: " ")
      }
    }

    return lines.joined(separator: "\n")
  }
}

```

[`chunks(ofCount:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Chunked.md) is from swift-algorithms.

> **Example**
>
> ```swift
> let A = ShapedArray([
> 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
> ], shape: 2, 3, 2, 4)
> 
> print(A)
> 
> ```
> 
> prints
> 
> ```swift
> ⎛ ⎛ ⎛ 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 ⎠ ⎠ ⎠
> 
> ```

Maybe with a blank line between each sub-matrix to better separate them visually.

---

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [August 22, 2024, 3:24am UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/11 "2024-08-22T03:24:26Z")

</div>

Here's my attempt based on your example but without using the swift-algorithms package. I don't understand how you are applying the brackets. What is the `.cycled()` method in the switch statement?

```swift
func description<T>(array: [T], shape: [Int]) -> String {
    var shape = shape
    let chunkSize = shape.popLast() ?? 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() ?? 0

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

    var descr = ""

    for (n, line) in lines.enumerated() {
        if n == 0 {
            descr += "⎡ " + line + " ⎤" + "\n"
        } else if n == 5 {
            descr += "⎣ " + line + " ⎦" + "\n"
        } else {
            descr += "⎜ " + line + " ⎥" + "\n"
        }
    }

    return descr
}

let numbers: [Float] = [1, 2, 3,
                        4, 5, 6,

                        7, 8, 9,
                        10, 11, 12,

                        13, 14, 15,
                        16, 17, 18]

let shape = [3, 2, 3]

let d = description(array: numbers, shape: shape)
print(d)

```

```plaintext
⎡ 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 ⎦

```

---

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [August 22, 2024, 3:31am UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/12 "2024-08-22T03:31:14Z")

</div>

In the image, how do you get the parentheses to be continuous between the lines? Is the image a screenshot of your terminal or is it showing something else? When I print out brackets or parentheses in the terminal, there are gaps between the lines as shown below. But in your screenshot there are no gaps between the lines.

```plaintext
⎡ 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 ⎦

```

---

<div class="post-metadata">

**Author:** ![xAlien95](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xalien95/32/12558_2.png) [@xAlien95](https://forums.swift.org/u/xAlien95)\
**Post date:** [August 22, 2024, 5:31am UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/13 "2024-08-22T05:31:57Z")

</div>

> [@wigging](#):
>
> What is the `.cycled()` method in the switch statement?

My bad, [`cycled()`](https://github.com/apple/swift-algorithms/blob/main/Guides/Cycle.md) is from swift-algorithms too. It returns a never-ending sequence cycling the provided sequence.

> [@wigging](#):
>
> In the image, how do you get the parentheses to be continuous between the lines?

@ibex is probably using either LaTeX or some derivation to pretty print the matrices.

---

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [August 22, 2024, 2:00pm UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/14 "2024-08-22T14:00:18Z")

</div>

I would like to avoid using swift-algorithms so here's my attempt at handling the brackets with just using Swift. It works for 2D and 3D arrays but brackets for higher dimensions are not captured.

```swift
func description<T>(array: [T], shape: [Int]) -> String {
    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() ?? 0

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

    var descr = ""
    let dim = shape.count
    let lastRow = shape.reduce(1, *) / chunkSize
    let lastSubRow = shape.reversed()[1]

    for (n, line) in lines.enumerated() {
        let n = n + 1
        if n == 1 {
            // First row brackets
            let prepend = String(repeating: "⎡ ", count: dim - 1)
            let append = String(repeating: " ⎤", count: dim - 1)
            descr += prepend + line + append + "\n"
        } else if n == lastRow {
            // Last row brackets
            let prepend = String(repeating: "⎣ ", count: dim - 1)
            let append = String(repeating: " ⎦", count: dim - 1)
            descr += prepend + line + append + "\n"
        } else if n % lastSubRow == 0 {
            // Last row brackets on subarray
            let prepend = String(repeating: "⎜ ", count: dim - 2)
            let append = String(repeating: " ⎥", count: dim - 2)
            descr += prepend + "⎣ " + line + " ⎦" + append + "\n"
        } else if n % lastSubRow == 1 {
            // First row brackets on subarray
            let prepend = String(repeating: "⎜ ", count: dim - 2)
            let append = String(repeating: " ⎥", count: dim - 2)
            descr += prepend + "⎡ " + line + " ⎤" + append + "\n"
        } else {
            let prepend = String(repeating:"⎜ ", count: dim - 1)
            let append = String(repeating: " ⎥", count: dim - 1)
            descr += prepend + line + append + "\n"
        }
    }

    return descr
}

```

Here's a 2D example:

```swift
let numbers: [Float] = [1, 2, 3,
                        42.8, 5, 6]

let shape = [2, 3]

let d = description(array: numbers, shape: shape)
print(d)

```

```plaintext
⎡ 1.0 2.0 3.0 ⎤
⎣ 42.8 5.0 6.0 ⎦

```

Here's a 3D example:

```swift
let numbers: [Float] = [1, 2, 3,
                        4, 5, 6,

                        7, 8, 9,
                        10, 11, 12,

                        13, 14, 15,
                        16, 17, 18]

let shape = [3, 2, 3]

let d = description(array: numbers, shape: shape)
print(d)

```

```plaintext
⎡ ⎡ 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 ⎦ ⎦

```

And here is a 4D example. Notice the brackets are not captured for the extra dimension.

```swift
let numbers: [Float] = [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]

let shape = [2, 3, 2, 4]

let d = description(array: numbers, shape: shape)
print(d)

```

```plaintext
⎡ ⎡ ⎡ 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 ⎦ ⎦ ⎦

```

Any suggestions on how to print the brackets for higher dimensions without using the swift-algorithms package?

---

<div class="post-metadata">

**Author:** ![xAlien95](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xalien95/32/12558_2.png) [@xAlien95](https://forums.swift.org/u/xAlien95)\
**Post date:** [August 22, 2024, 6:56pm UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/15 "2024-08-22T18:56:30Z")

</div>

Using modulo as you did in your implementation, you can generate the array of row counts `rows` (in your 4D example it would be [12, 6, 2]) and do something like:

```swift
for (n, line) in lines.enumerated() {
  for row in rows.reversed() {
    switch n % row {
    case 0: descr += "⎡ "
    case row - 1: descr += "⎣ "
    default: descr += "⎜ "
    }
  }

  descr += line

  for row in rows {
    switch n % row {
    case 0: descr += " ⎤"
    case row - 1: descr += " ⎦"
    default: descr += " ⎥"
    }
  }

  descr += "\n"
}

```

Make sure to handle the case where the second to last element in `shape` is 1 though. Also, do you need the extra newline at the end of the description string?

---

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [August 23, 2024, 1:03am UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/16 "2024-08-23T01:03:54Z")

</div>

Based on your example I now have the function shown below. I removed the newline for the last row. I also used parentheses instead of square brackets. I check for `row == 1` to handle cases where second to last element in `shape` is 1.

```swift
func description<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 = cumulativeProd(r)

    var descr = ""

    for (n, line) in lines.enumerated() {
        for row in rows.reversed() {
            if row == 1 {
                descr += "( "
                continue
            }

            switch n % row {
            case 0:
                descr += "⎛ "
            case row - 1:
                descr += "⎝ "
            default:
                descr += "⎜ "
            }
        }

        descr += line

        for row in rows {
            if row == 1 {
                descr += " )"
                continue
            }

            switch n % row {
            case 0:
                descr += " ⎞"
            case row - 1:
                descr += " ⎠"
            default:
                descr += " ⎟"
            }
        }

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

    return descr
}

```

The function to calculate the cumulative product of an array is:

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

```

---

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [August 23, 2024, 2:11am UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/17 "2024-08-23T02:11:37Z")

</div>

It would make it easier to read the print output if a newline is placed after each dimension. Something like this:

```plaintext
⎛ ⎛ ⎛ 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 ⎠ ⎠ ⎠

```

---

<div class="post-metadata">

**Author:** ![ibex](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ibex/32/30439_2.png) [@ibex](https://forums.swift.org/u/ibex)\
**Post date:** [August 23, 2024, 6:58am UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/18 "2024-08-23T06:58:55Z")

</div>

That really looks good. 🙂

Here is my take, which prints the array by using **MathML**.

 ![MyArray](https://global.discourse-cdn.com/swift/original/3X/8/a/8aa8d1ada0c514eefc8a1282bf21d62f839fcc42.png)

> **For the More Curious**
>
> > **Driver.swift**
> >
> > ```swift
> > //
> > // Driver.swift
> > // MyArray
> > //
> > // Created by ibex on 22/8/2024.
> > //
> > 
> > @main
> > enum Driver {
> > static func main () {
> > let shape = [3, 3, 3]
> > let v1 = MyArray (shape: shape) {
> > random_string()
> > }
> >         
> > let v2 = MyArray (shape: shape) {
> > random_int ()
> > }
> > 
> > let v3 = MyArray (shape: shape) {
> > random_int ()
> > }
> >         
> > v1.print ()
> > print ()
> >         
> > let u = v1.printMathML ()
> > let v = v2.printMathML ()
> > let w = v3.printMathML ()
> > print ("--> MathML:")
> > print (u)
> > print (ML.Operator.times)
> > print (v)
> > print (ML.Operator.equals)
> > print (w)
> > }
> > }
> > 
> > private func random_int () -> Int {
> > Int.random(in: 0..<1024)
> > }
> > 
> > private func random_string () -> String {
> > let u: [String] = ["u", "v", "x", "y", "z", "p", "q", "r", "s", "t", "w", "i", "j", "k", "delta"]
> > let i = Int.random (in: 0..<u.count)
> > return u [i]
> > }
> > 
> > extension Int: ML.Content {
> > var value: String {
> > ML.Number (self).value
> > }
> > }
> > 
> > extension Double: ML.Content {
> > var value: String {
> > ML.Number (self).value
> > }
> > }
> > 
> > extension String: ML.Content {
> > var value: String {
> > ML.Variable (self).value
> > }
> > }
> > 
> > ```
> 
> > **MyArray.swift**
> >
> > ```swift
> > //
> > // MyArray.swift
> > // MyArray
> > //
> > // Created by ibex on 22/8/2024.
> > //
> > 
> > import Foundation
> > 
> > struct MyArray <T: ML.Content> {
> > let store: [T]
> > let shape: [Int]
> > }
> > 
> > extension MyArray {
> > init (shape: [Int], generator: () -> T) {
> > self.shape = shape
> > let M = Self.size (from: shape)
> > self.store = .init (unsafeUninitializedCapacity: M) {buffer,initializedCount in
> > for i in 0..<M {
> > buffer [i] = generator ()
> > }
> > initializedCount = M
> > }
> > }
> > }
> > 
> > extension MyArray {
> > static func size (from shape: [Int]) -> Int {
> > guard !shape.isEmpty else {
> > return 0
> > }
> > let u = 1
> > let v: Int = shape.reduce (u) {
> > $0 * $1
> > }
> > return v
> > }
> > }
> > 
> > extension MyArray {
> > func array (index: Int, shape: [Int]) -> MyArray {
> > let size = Self.size(from: shape)
> > let offset = index * size
> > assert (offset < store.count)
> > 
> > var store: [T] = []
> > for i in 0..<size {
> > store.append (self.store [i + offset])
> > }
> > return MyArray (store:store, shape: shape)
> > }
> > }
> > 
> > extension MyArray {
> > func print () {
> > let size = Self.size (from: shape)
> > Swift.print ("-->", shape, size, store)
> > _print ()
> > }
> > }
> > 
> > extension MyArray {
> > private func _print () {
> > let size = Self.size (from: shape)
> > guard size > 0 else {
> > return
> > }
> >         
> > if shape.count == 1 {
> > Swift.print ("-->", shape, size, store)
> > return
> > }
> >                 
> > var shape = self.shape
> > let M = shape.removeFirst()
> >         
> > for i in 0..<M {
> > let array = array (index: i, shape: shape)
> > array._print ()
> > }
> > Swift.print ()
> > }
> > }
> > 
> > ```
> 
> > **MyArray+PrintML.swift**
> >
> > ```swift
> > //
> > // MyArray+PrintML.swift
> > // MyArray
> > //
> > // Created by ibex on 22/8/2024.
> > //
> > 
> > extension MyArray {
> > func printMathML (display: ML.Math.Display = .inline) -> String {
> > let size = Self.size (from: shape)
> > Swift.print ("-->", shape, size, store)
> >         
> > let u = emitML ()
> > let v = ML.Math (u, display: display)
> > return v.value
> > }
> > }
> > 
> > extension MyArray {
> > private func emitML () -> ML.Content {
> > let size = Self.size (from: shape)
> > guard size > 0, shape.count > 0 else {
> > return ML.Text ("empty array")
> > }
> >         
> > #if false
> > Swift.print ("-->", shape, size, store)
> > #endif
> >         
> > if shape.count == 1 {
> > // 1 x P
> > let row = ML.Row (store)
> > return ML.Table ([row])
> > }
> > else if shape.count == 2 {
> > // P x Q
> > var shape = self.shape
> > let M = shape.removeFirst()
> > 
> > var rows: [ML.Content] = []
> > for i in 0..<M {
> > let array = array (index: i, shape: shape)
> > let row = ML.Row (array.store)
> > rows.append (row)
> > }
> > return ML.Table (rows)
> > }
> > else {
> > // P x Q x R ...
> > var shape = self.shape
> > let M = shape.removeFirst()
> >             
> > var rows: [ML.Content] = []
> > for i in 0..<M {
> > let array = array (index: i, shape: shape)
> > let u = array.emitML ()
> > rows.append (ML.Row ([u]))
> > }
> > return ML.Table (rows)
> > }
> > }
> > }
> > 
> > ```
> 
> > **ML**
> >
> > ```swift
> > //
> > // ML.swift
> > // MyArray
> > //
> > // Created by ibex on 22/8/2024.
> > //
> > 
> > enum ML {
> > protocol Content {
> > var value: String {get}
> > }
> > }
> > 
> > extension ML {
> > struct Element: Content {
> > let value: String
> >         
> > init () {
> > self.value = ""
> > }
> > 
> > init (tag: String, value: String) {
> > self.value = "<\(tag)>\(value)</\(tag)>"
> > }
> >         
> > init (tag: String, attributes: String, value: String) {
> > self.value = "<\(tag) \(attributes)>\(value)</\(tag)>"
> > }
> > }
> > }
> > 
> > extension ML {
> > struct Error: Content {
> > var value: String {
> > content.value
> > }
> > 
> > var content: Element
> > 
> > init (_ u: String) {
> > self.content = Element (tag: "merror", value: "\(u)")
> > }
> > }
> > }
> > 
> > extension ML {
> > struct Number: Content {
> > var value: String {
> > content.value
> > }
> > 
> > var content: Element
> > 
> > init (_ u: some Numeric) {
> > self.content = Element (tag: "mn", value: "\(u)")
> > }
> > }
> > }
> > 
> > extension ML {
> > struct Text: Content {
> > var value: String {
> > content.value
> > }
> > 
> > var content: Element
> > 
> > init (_ u: String) {
> > self.content = Element (tag: "mtext", value: "\(u)")
> > }
> > }
> > }
> > 
> > extension ML {
> > struct Variable: Content {
> > var value: String {
> > content.value
> > }
> > 
> > var content: Element
> > 
> > init (_ u: String) {
> > if u.count == 1 {
> > self.content = Element (tag: "mi", value: "\(u)")
> > }
> > else if u.count > 1 {
> > var vv : [Element] = []
> > for i in u {
> > vv.append (Element (tag: "mi", value: String (i)))
> > }
> > self.content = Element (tag: "mrow", value: vv.map {$0.value}.joined ())
> > }
> > else {
> > self.content = Error ("empty variable string").content
> > }
> > }
> > }
> > }
> > 
> > extension ML {
> > struct Operator: Content {
> > var value: String {
> > content.value
> > }
> > 
> > var content: Element
> > 
> > init (_ u: Op) {
> > self.content = Element (tag: "mo", value: "\(u.rawValue)")
> > }
> >         
> > init (_ u: String) {
> > self.content = Element (tag: "mo", value: "\(u)")
> > }
> >         
> > enum Op: String {
> > case plus = "+"
> > case minus = "&#x2212;"
> > case equals = "="
> > case times = "&#xD7;"
> > }
> >         
> > static var plus = Self (.plus).value
> > static var minus = Self (.minus).value
> > static var equals = Self (.equals).value
> > static var times = Self (.times).value
> > }
> > }
> > 
> > ```
> 
> > **ML+Table.swift**
> >
> > ```swift
> > //
> > // ML+Table.swift
> > // MyArray
> > //
> > // Created by ibex on 23/8/2024.
> > //
> > 
> > extension ML {
> > struct Row: Content {
> > var value: String {
> > content.value
> > }
> > 
> > var content: Element
> >         
> > init (_ u: [Content]) {
> > let v = u.map {Element (tag: "mtd", value: $0.value)}.map {$0.value}.joined()
> > self.content = Element (tag: "mtr", value: v)
> > }
> > }
> > }
> > 
> > extension ML {
> > struct Table: Content {
> > var value: String {
> > content.value
> > }
> > 
> > var content: Element
> >         
> > init (_ u: [Content]) {
> > let v = u.map {$0.value}.joined()
> > let t = Element (tag: "mtable", value:v)
> > self.content = Element (tag: "mfenced", attributes: "open=\"(\" close=\")\" separators=\"\"", value: t.value)
> > }
> > }
> > }
> > 
> > extension ML {
> > struct Math {
> > enum Display: String {
> > case block, inline
> > }
> > var value: String {
> > content.value
> > }
> > 
> > var content: Element
> > 
> > init (_ u: Content, display: Display = .block) {
> > self.content = ML.Element (tag: "math", attributes: "xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"\(display)\"", value: u.value)
> > }
> > }
> > }
> > 
> > ```

---

<div class="post-metadata">

**Author:** ![wigging](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wigging/32/34513_2.png) [@wigging](https://forums.swift.org/u/wigging)\
**Post date:** [August 23, 2024, 12:57pm UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/19 "2024-08-23T12:57:06Z")

</div>

So your `print` function prints out MathML syntax is that correct? How are you rendering the MathML, do you use a WebView, a SwiftUI view, or something else? And what if you want to view your array in the terminal, do you have a way to render it in the terminal?

---

<div class="post-metadata">

**Author:** ![xAlien95](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xalien95/32/12558_2.png) [@xAlien95](https://forums.swift.org/u/xAlien95)\
**Post date:** [August 23, 2024, 2:24pm UTC](https://forums.swift.org/t/print-an-n-dimensional-array/74082/20 "2024-08-23T14:24:45Z")

</div>

The algorithm shouldn't change that much: change your `cumulativeProd` to

```swift
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)
        }
    }
}

```

and use a counter (`n` in the following snippet)

```swift
var n = 0
let last = rows.last! // this is a simplification: make sure you
                      // correctly handle the case in which rows 
                      // is empty 

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)
}

```

[Next page](https://forums.swift.org/t/print-an-n-dimensional-array/74082.md?page=2)
