21
\$\begingroup\$

I like to pretty print multidimensional arrays, like this:

[ [ [1, 2, 3],
    [4, 5, 6] ],
  [ [7, 8, 9],
    [6, 4, 2] ] ]

But it's a pain to do by hand and it'd be nice to have a program that does this for me. Your challenge is to create a program that does this for me, taking a multidimensional array containing only positive integers and prettyprinting it.

Specifically, an array of depth 1 is printed joined by , with [ prepended and ] appended:

[1, 2, 3]

An array of depth \$n+1\$, which contains at least one array of depth \$n\$, has its subarrays prettyprinted, joined by newlines and indented by two spaces. All but the last subarray have a comma appended, the last has ] appended, and the first has its first line indented with [ instead of two spaces:

enter image description here

Here's a reference implementation:

function recursivePrettyPrint(array){
  if(array.every(x => typeof x == "number")){
    return `[${array.join(', ')}]`;
  } else {
    return array.map((item, index) => {
      let result = recursivePrettyPrint(item) + ',';
      result = result.split`\n`;
      if(index == 0){
        result[0] = '[ ' + result[0];
      } else {
        result[0] = '  ' + result[0];
      }
      for(let i = 1; i < result.length; i++){
        result[i] = '  ' + result[i]
      }
      return result.join('\n');
    }).join('\n').slice(0,-1) + ' ]';
  }
}

function change(){
  let array = JSON.parse(document.getElementById('input').value);
  let output = document.getElementById('output');
  output.innerText = recursivePrettyPrint(array);
}
<textarea id=input></textarea>

<button id=run onclick=change()>Pretty Print</button>

<pre id=output></pre>

Numbers may be multiple digits. The input will always be orthogonal/rectangular, and you may take its dimensions as well. Trailing spaces on lines are allowed.

Testcases

[[892, 759], [962, 251]] ->
[ [892, 759],
  [962, 251] ]

[118, 922, 619] ->
[118, 922, 619]

[[966, 639, 616, 255], [622, 483, 87, 241], [453, 870, 728, 725], [163, 936, 48, 967], [261, 833, 87, 200]] -> 
[ [966, 639, 616, 255],
  [622, 483, 87, 241],
  [453, 870, 728, 725],
  [163, 936, 48, 967],
  [261, 833, 87, 200] ]

[[[[[912, 547], [366, 754]], [[723, 536], [779, 238]]], [[[559, 392], [602, 709]], [[692, 915], [412, 302]]]], [[[[3, 504], [936, 83]], [[352, 442], [425, 375]]], [[[380, 440], [793, 762]], [[850, 321], [780, 457]]]]] ->
[ [ [ [ [912, 547],
        [366, 754] ],
      [ [723, 536],
        [779, 238] ] ],
    [ [ [559, 392],
        [602, 709] ],
      [ [692, 915],
        [412, 302] ] ] ],
  [ [ [ [3, 504],
        [936, 83] ],
      [ [352, 442],
        [425, 375] ] ],
    [ [ [380, 440],
        [793, 762] ],
      [ [850, 321],
        [780, 457] ] ] ] ]

[[[128, 910, 664, 658], [172, 238, 564, 492], [325, 384, 566, 90]], [[876, 819, 764, 105], [583, 528, 731, 839], [480, 126, 692, 875]], [[215, 84, 268, 504], [400, 674, 997, 526], [799, 692, 193, 296]], [[943, 185, 567, 188], [118, 200, 879, 409], [116, 493, 62, 343]]] -> 
[ [ [128, 910, 664, 658],
    [172, 238, 564, 492],
    [325, 384, 566, 90] ],
  [ [876, 819, 764, 105],
    [583, 528, 731, 839],
    [480, 126, 692, 875] ],
  [ [215, 84, 268, 504],
    [400, 674, 997, 526],
    [799, 692, 193, 296] ],
  [ [943, 185, 567, 188],
    [118, 200, 879, 409],
    [116, 493, 62, 343] ] ]
\$\endgroup\$
4
  • \$\begingroup\$ May I assume all numbers are positive? \$\endgroup\$
    – tsh
    Feb 18, 2022 at 7:13
  • 3
    \$\begingroup\$ Is the array guaranteed to be nonempty at any level? \$\endgroup\$
    – alephalpha
    Feb 18, 2022 at 7:16
  • 3
    \$\begingroup\$ @alephalpha To my understanding, since the question specify that "The input will always be orthogonal/rectangular", and "array containing only numbers", you can safely assume that any array is non-empty. Otherwise, The input will not contain any numbers in it or it will not be rectangular any more. \$\endgroup\$
    – tsh
    Feb 18, 2022 at 7:47
  • 2
    \$\begingroup\$ Somewhat related \$\endgroup\$
    – DLosc
    Feb 18, 2022 at 22:40

10 Answers 10

10
\$\begingroup\$

Python 2, 74 73 72 bytes

-1 thanks to loopy walt

-1 thanks to dingledooper

f=lambda l,s=',\n  ':l<[f]and`l`or'[ %s ]'%s.join(f(r,s+'  ')for r in l)

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ -1 using that numbers<funcs<lists \$\endgroup\$
    – loopy walt
    Feb 18, 2022 at 16:42
  • \$\begingroup\$ Save one with '[ %s ]'%s instead of '[ '+s+' ]' \$\endgroup\$ Feb 18, 2022 at 21:09
5
\$\begingroup\$

JavaScript (SpiderMonkey), 69 bytes

(g=j=>a=>+(u=uneval(a))[1]?u:`[ ${a.map(g(j+'  ')).join(j)} ]`)`,
  `

Try it online!

This answer assume the array only contains positive integers.

\$\endgroup\$
4
\$\begingroup\$

05AB1E, 38 37 bytes

dΔ€`}\„, ý'[ì']«N©F„
,®N-·úRý„[ ì„ ]«

Try it online or verify all test cases.

Explanation:

dΔ€`}\...N         # Determine the depth of the input:
d                  #  Transform each inner-most integer to a 1 (with >=0 check)
 Δ                 #  Loop until the result no longer changes:
  €`               #   Flatten it one level down
    }\             #  After the loop: discard the resulting list of 1s
„, ý               # Join each inner-most list of the (implicit) input with
                   # ", "-delimiter
    '[ì           '# Prepend a "[" in front of each string
    ']«           '# Append a "]" after each string
         N         # Push the reached index of the earlier loop, which is the
                   # depth of the input-list - 1
 ©                 # Store this depth-1 in variable `®` (without popping)
  F                # Loop `N` in the range [0,®):
   „\n,            #  Push string "\n,"
       ®N-         #  Subtract `N` from `®`
          ·        #  Double it
           ú       #  Pad the string with that meany leading spaces
            R      #  Reverse it
             ý     #  Join each inner-most list of strings with this as delimiter
              „[ ì #  Prepend a "[ " in front of each string
              „ ]« #  Append a "]" after each string
                   # (after which the result is output implicitly)

dΔ€`}\...N to determine the depth is taken from this answer of mine.

\$\endgroup\$
3
\$\begingroup\$

Pari/GP, 96 bytes

f(a,i=",\n  ")=print1(if(#a[1]',l=!print1("[ ");[(l--&&print1(i))+f(b,Str(i"  "))|b<-a];" ]",a))

Try it online!

A port of @att's Python answer.

\$\endgroup\$
3
\$\begingroup\$

Pip -x, 37 35 bytes

"[]"J:0*@a?(fMaJ:",
")WRsRn"
  "aJk

Recursive full program; takes the array as a nested list in Pip format from the command-line. Attempt This Online!

Explanation

Using in place of newlines for better readability:

"[]"J:0*@a?(fMaJ:",¶")WRsRn"¶  "aJk
         a                           ; Current list
        @                            ; First element
      0*                             ; Times 0
          ?                          ; Is truthy?
                                     ; If so, first element is a list:
            f                        ;  Apply the main function recursively
             M                       ;  to each element of
              a                      ;  the current list
               J:                    ;  Join the results on
                 ",¶"                ;  comma + newline
           (         )WR             ;  Wrap that string in
                        s            ;  space
                         R           ;  Replace
                          n          ;  each newline with
                           "¶  "     ;  newline plus two spaces
                                     ; Else, first element is a number:
                                a    ;  Current list
                                 J   ;  joined on
                                  k  ;  comma + space
"[]"                                 ; Open bracket + close bracket
    J:                               ; joined on the above result
\$\endgroup\$
3
\$\begingroup\$

Retina 0.8.2, 66 bytes

,|\[\B
$& 
\B]
 ]
], (?=(.+))
],¶$1_
T`d ,``.+_
+`\[]

+`]_
_  
_

Try it online!

Explanation

The first four lines, which add spaces where necessary, are taken from Neil's Retina answer.

Now, observe that:

  • We need to add a newline and an indent after every occurrence of ],
  • The indentation level is equal to the nesting level at that point in the list, which can be calculated by looking at the number of brackets in the remaining part of the list: nesting level = (# of remaining close brackets) - (# of remaining open brackets)

So:

], (?=(.+))

Match ], and capture the rest of the line in group 1; then:

],¶$1_

Replace that with ], followed by a newline, the contents of group 1, and an underscore for a separator.

T`d ,``.+_

Transliteration stage: Replace digits, space, and comma with nothing (i.e. delete them) when they appear in any match of .+_ (i.e. before _ on a line).

+`\[]

Repeatedly delete empty matched brackets (which can only occur in the before-_ context) until none are left.

+`]_
_  

Repeatedly replace close-bracket followed by underscore with underscore followed by two spaces. This has the effect of translating each level of nesting (represented by the unmatched close-brackets) into two spaces of indentation.

_

Delete the underscores.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ It's actually possible to count the ]s and [s fairly directly using .NET's balancing groups; I added a section to my answer to show how. \$\endgroup\$
    – Neil
    Feb 19, 2022 at 8:52
3
\$\begingroup\$

Retina 0.8.2, 76 bytes

,|\[\B
$& 
\B]
 ]
+%1`((.*?\[)((\[)|(?<-4>])|[^][])+(?(4)^),) \[
$1¶$.2$*  [

Try it online! Link includes test cases. Assumes input contains no spaces (seems awkward to ask for spaces given that this is a formatting challenge). Explanation:

,|\[\B
$& 
\B]
 ]

Insert the necessary spaces.

+%1`

Repeatedly replace the first occurrence on each line until no more replacements can be made:

((.*?\[)((\[)|(?<-4>])|[^][])+(?(4)^),) \[

Match as little indentation (up to and including the parent [) as possible, as many subarrays as possible, then the start of a subarray.

$1¶$.2$*  [

Wrap that last subarray to a new line, giving it the indentation it needs converted to spaces (it would have contained [s).

60 bytes by using @DLosc's observation that the amount of indent can be calculated from the remaining square brackets:

,|\[\B
$& 
\B]
 ]
], (?=((\[)|(?<-2>])|(){2}]|.)+)
],¶$#3$* 

Try it online! Link includes test cases. Explanation: The first two stages add the necessary spaces as above.

], (?=((\[)|(?<-2>])|(){2}]|.)+)

Match a closing square bracket with a comma, count the number of [s in $#2, ignore that many ]s, then count two (spaces of indent) for each extra ].

],¶$#3$* 

Break the line after the comma and replace the space with the desired amount of indent.

\$\endgroup\$
3
\$\begingroup\$

Charcoal, 75 60 bytes

≔⭆¹SθF[]≔⪫E⪪θι∨κ ιθ≔⪪✂θ¹±¹¦¹], θ⪫Eθ⁺×  ⁻№⪫…θκω[⁺κ№⪫…θκω]ι],¶

Try it online! Link is to verbose version of code. Explanation: Uses @Dlosc's observation that the indent can be calculated from the difference in numbers of [s and ]s.

≔⭆¹Sθ

Input the array and perform Python stringification on it, which joins the elements with , but wraps them in [ and ] without spaces.

F[]≔⪫E⪪θι∨κ ιθ

Insert spaces between pairs of [ or ]. Unfortunately this also adds a leading and a trailing space.

≔⪪✂θ¹±¹¦¹], θ

Trim away that leading and trailing space, then split on ], .

Eθ⁺×  ⁻№⪫…θκω[⁺κ№⪫…θκω]ι],¶

Map over the pieces of strings, calculating the indent from the number of [s in all the previous strings minus the number of ]s in all the previous strings (adjusted because they've been split on ],), then output the strings each on their own line with the ], restored (but not on the last line).

\$\endgroup\$
2
\$\begingroup\$

Python3, 230 bytes:

E=enumerate
def f(x,d=[]):
 m=len(d)-(min(i+1 for i,a in E(d)if not any(d[i+1:]))if any(d)else 0)
 return ' '*((len(d)-m)*2)+'[ '*m+str(x)if all(int==type(i)for i in x)else'\n'.join(f(a,d+[i])+','*(i<len(x)-1)for i,a in E(x))+' ]'

Try it online!

\$\endgroup\$
2
1
\$\begingroup\$

Julia 1.0, 60 bytes

>(l,s=",\n  ")=try.~l;"$l"catch;"[ $(join(l.>s*"  ",s)) ]"end

Try it online! (with a literal \n)

port of att's great answer

.~l tries to apply ~ to each element of l, which only works if l is not nested (shorter than l[1] isa Int)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.