39
\$\begingroup\$

A simple challenge for your Monday evening (well, or Tuesday morning in the other half of the world...)

You're given as input a nested, potentially ragged array of positive integers:

[1, [[2, 3, [[4], 5], 6, [7, 8]], 9, [10, [[[11]]]], 12, 13], 14]

Your task is to determine its depth, which is the greatest nesting-depth of any integer in the list. In this case, the depth of 11 is 6, which is largest.

You may assume that none of the arrays will be empty.

You may write a program or function, taking input via STDIN (or closest alternative), command-line argument or function argument and outputting the result via STDOUT (or closest alternative), function return value or function (out) parameter.

Input may be taken in any convenient list or string format that supports non-rectangular arrays (with nested arrays of different depths), as long as the actual information isn't preprocessed.

You must not use any built-ins related to the shape of arrays (including built-ins that solve this challenge, that get you the dimensions of a nested array). The only exception to this is getting the length of an array.

Standard rules apply.

Test Cases

[1]                                                               -> 1
[1, 2, 3]                                                         -> 1
[[1, 2, 3]]                                                       -> 2
[3, [3, [3], 3], 3]                                               -> 3
[[[[1], 2], [3, [4]]]]                                            -> 4
[1, [[3]], [5, 6], [[[[8]]]], 1]                                  -> 5
[1, [[2, 3, [[4], 5], 6, [7, 8]], 9, [10, [[[11]]]], 12, 13], 14] -> 6
[[[[[[[3]]]]]]]                                                   -> 7
\$\endgroup\$
5
  • 4
    \$\begingroup\$ After discussion in chat I've decided to allow length built-ins, because some languages require them to iterate over an array cleanly. \$\endgroup\$ Feb 8, 2016 at 23:09
  • 5
    \$\begingroup\$ Just for general education: is APL's built-in primitive for exactly this. \$\endgroup\$
    – Adám
    Feb 9, 2016 at 16:09
  • \$\begingroup\$ @MartinBüttner I've run into a tiny problem. I started doing this in java, unfourtunetly when testing inputs the commas are causing it to split the inputs into multiple command line arguments rather then one. Can I use the escape character \ in the inputs? EDIT: nevermind just tried it like that. That doesn't even work either. Darn can I not use CMD args? \$\endgroup\$ Feb 10, 2016 at 5:21
  • \$\begingroup\$ @AshwinGupta can't you wrap the command line argument in quotes? You can also read input from STDIN or submit a function that takes an actual array object as a parameter. \$\endgroup\$ Feb 10, 2016 at 10:39
  • \$\begingroup\$ @MartinBüttner oh I didn't know that quotes thing I'll try it out. Currently just using Scanner. (System.in). I believe that is a form of STDIN? \$\endgroup\$ Feb 10, 2016 at 22:02

53 Answers 53

1
2
1
\$\begingroup\$

CJam, 19 22 23 bytes

0l{_91=\93=-+_}%:e>

Similar idea to my MATL answer.

Thanks to Peter Taylor for removing 3 bytes

Try it here

0                            push a 0
l                            read line as string
{            }%              map this block on the string
  _91=\93=-                  1 if it's an opening bracket, -1 if closing
           +_                cumulative sum
               :e>           fold maximum function
\$\endgroup\$
0
1
\$\begingroup\$

Perl 5, 34 bytes

32, plus two for -p

{s&]\[|[^][]&&g&&redo}$_=@a=/]/g

Stolen from Digital Trauma's Retina answer… which is 26% shorter than this. :-)

Or, equally:

{s&]\[|[^][]&&g&&redo}$_=y///c/2
\$\endgroup\$
2
  • \$\begingroup\$ @Cyoce, why? ] doesn't need escaping, except in brackets. \$\endgroup\$
    – msh210
    Oct 7, 2016 at 6:11
  • \$\begingroup\$ @Cyoce, s&...&...&g is the substitution operator. See perldoc.perl.org/perlop.html \$\endgroup\$
    – msh210
    Oct 7, 2016 at 9:40
1
\$\begingroup\$

Ruby, 51 characters

(Started as improvement suggestion for Doorknob's Ruby answer but ended differently. So I posted it as separate answer. Upvotes for the depth counting idea (?\\<=>$&, descending from '] ['.index(c)) should go to the original answer.)

m=i=0
gets.gsub(/\[|\]/){m=[m,i+=?\\<=>$&].max}
p m

Input: string, output: number.

Sample run:

bash-4.3$ ruby -e 'm=i=0;gets.gsub(/\[|\]/){m=[m,i+=?\\<=>$&].max};p m' <<< '[1, [[2, 3, [[4], 5], 6, [7, 8]], 9, [10, [[[11]]]], 12, 13], 14]'
6
\$\endgroup\$
1
\$\begingroup\$

Minkolang 0.15, 31 29 24 bytes

Overhauled my algorithm upon inspiration by Luis Mendo's CJam answer and saved 5 bytes!

od5&j$ZN.d"["=$r"]"=~++d

Try it here!

Explanation

Essentially, what this code does is keep a running total with +1 for each [ and -1 for each ], keeping track of the maximum value reached, outputting that maximum at the end. Looping is handled by the toroidal nature of Minkolang's codebox.

od           Take character from input and duplicate it (0 if input is empty)
  5&         Pop top of stack and skip the following five spaces if 0
    j$Z      Push the maximum value of the stack
       N.    Output as number and stop.

  d                  Duplicate top of stack for character tests
   "["=              +1 if the character is [
       $r            Swap top two items of stack
         "]"=~       -1 if the character is ]
              ++     Add twice
                d    Duplicate top of stack for the running total
\$\endgroup\$
1
\$\begingroup\$

Ruby, 41 characters

f=->a,d=1{a.map{|e|f[e,d+1]rescue d}.max}

Parameter: array, return: number.

Sample run:

2.1.5 :001 > f=->a,d=1{a.map{|e|f[e,d+1]rescue d}.max}
 => #<Proc:0x0000000214d258@(irb):1 (lambda)> 

2.1.5 :002 > f[[1, [[2, 3, [[4], 5], 6, [7, 8]], 9, [10, [[[11]]]], 12, 13], 14]]
 => 6 
\$\endgroup\$
1
\$\begingroup\$

Oracle SQL 11.2, 133 bytes

SELECT MAX(d)FROM(SELECT SUM(DECODE(SUBSTR(:1,LEVEL,1),'[',1,']',-1,0))OVER(ORDER BY LEVEL)d FROM DUAL CONNECT BY LEVEL<=LENGTH(:1));

Un-golfed

SELECT MAX(d)
FROM   (
         SELECT SUM(DECODE(SUBSTR(:1,LEVEL,1),'[',1,']',-1,0))OVER(ORDER BY LEVEL) d 
         FROM   DUAL 
         CONNECT BY LEVEL<=LENGTH(:1)
       );

The CONNECT BY creates one row per character in the input string.

The SUBSTR isolates the character corresponding to the row number.

The DECODE translates each '[' to 1, each ']' to -1 and every other character to 0.

The analytic SUM sums each 1, -1 and 0 from the preceding rows, including the current row;

The MAX sums is the depth.

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

Java 8, 95

This is a lambda expression for a ToIntFunction<String>. Input is taken as a String in the OP's examples format.

s->{int d=e=-1;for(String t:s.split("[")){d=++e>d?e:d;e-=t.split("]",-1).length()-1;}return d;}

fairly straightfoward. Split the string using [ as the delimiter. For each of them, increment the counter e and compare it with the counter d, keeping the larger of them in d. Then split the current iteration's string using ] as the delimiter this time and subtract the number of extra splits from e.

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

Ruby, 42 characters

f=->a{a==a.flatten(1)?1:f[a.flatten(1)]+1}

Example:

irb(main):001:0> f.call([[[1,2],[2,3]],[[3,4],[5]]])
=> 3

And it's actually readable. :)

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Welcome to the site! \$\endgroup\$
    – DJMcMayhem
    Sep 7, 2017 at 17:02
1
\$\begingroup\$

Swift - 76 bytes

func d(_ a:[Any])->Int{return 1+(a.map{$0 is Int ?0:d($0 as![Any])}).max()!}

A recursive function that returns one plus the max of all non ints in the array using map.

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

Lua, 101 100 bytes

d,c=0,0 print((...):gsub(".",function(s)c=s=='['and c+1 or s==']'and c-1 or c d=c>d and c or d end))

Try it online!

How it works

-- Passed To Command Line: "[1, [[2, 3, [[4], 5], 6, [7, 8]], 9, [10, [[[11]]]], 12, 13], 14]"

-- Deepest Sub-Array Count, Current Depth
deepest, current = 0, 0

-- We abuse gsub() to iterate over every character in the input string and run a function for it
(...):gsub(".", function(s)
    -- The current depth is incremented if s (the current character) is equal to [, OR decremented if s is equal to ], OR left alone
    current = (s == '[' and current+1) or (s == ']'and current-1) or current
    -- If current is > deepest then deepest = current else deepest = current, we're using boolean logic like a ternary operator
    deepest = current > deepest and current or deepest
end)

print(deepest)
```
\$\endgroup\$
4
  • \$\begingroup\$ How are you taking input? Based on your example it seems like you might be expecting it to be stored in a specific variable? There has been a long standing consensus that this sort of input is only permitted in certain very specific contexts. For a list of ways to take input see this meta thread. \$\endgroup\$
    – Wheat Wizard
    Jul 31, 2020 at 2:40
  • \$\begingroup\$ Didn't realize that wasn't allowed, updated to reflect that. \$\endgroup\$
    – Benrob0329
    Jul 31, 2020 at 4:15
  • \$\begingroup\$ You can save one byte by replacing arg[1] with (...) \$\endgroup\$ Aug 21, 2020 at 16:50
  • \$\begingroup\$ I've heard that ... works in place of the args table, but it doesn't seem to work with 5.3. Is is a 5.4 feature? \$\endgroup\$
    – Benrob0329
    Aug 22, 2020 at 4:42
1
\$\begingroup\$

Haskell + hgl, 20 bytes

y=dPF(p 0)$P1<mx<m y

Takes input as a free monad of lists (Free List a).

Explanation

A ragged list can contain either a terminal element or a list of more ragged lists.

dPF is a function which takes a function to convert terminal elements and a function to convert lists of ragged lists and uses them to convert a ragged list. This is captured in the type signature:

dPF :: (a -> b) -> ([Free [] a] -> b) -> Free [] a -> b

We give it two functions, for terminal elements it is very simple, terminal elements always have depth 0, so we give it p 0, meaning always return zero.

For lists we get the maximum depth of each element and add 1 using the function we are defining recursively. P1<mx<m y.

Reflections

  • We spend 6 bytes to envoke dpF properly dPF()$. This should have an infix function. Even if the operator precedence was bad it would have saved a byte.
  • It's not an issue here because the lists can't be empty, but we should have a maximum with default function.
  • Max with map also seems common enough to implement.
  • Might have not saved bytes here, but this is the second time I have wanted a fix function. Should probably add that before I hit a place where I really need it.
\$\endgroup\$
1
\$\begingroup\$

tinylisp, 48 bytes

(load library
(d f(q((x)(i(v x)0(+ 1(max(map f x

Try it online!

-11 from Dlosc

gets depth of a quoted list.

\$\endgroup\$
3
  • \$\begingroup\$ Since arrays are guaranteed nonempty and integers are guaranteed positive, you can replace the type check with (v x). Evaling an int returns it unchanged; evaling a list of ints returns nil + error message because the head of the list should be a function or macro. Try it online! \$\endgroup\$
    – DLosc
    Feb 1, 2022 at 18:45
  • \$\begingroup\$ (Also, for future reference, there's a type? function that is a little shorter than using type with e.) \$\endgroup\$
    – DLosc
    Feb 1, 2022 at 18:46
  • \$\begingroup\$ @DLosc i was skeptical about whether errors halted execution because i never really got where the error were coming from. anyway, thanks. \$\endgroup\$
    – Razetime
    Feb 2, 2022 at 1:46
1
\$\begingroup\$

Wolfram Language (Mathematica), 16 bytes

Max[#0/@-1^#]+1&

Try it online!

The relatively low precedence of prefix - means -1^# is evaluated before the high-precedence /@.

\$\endgroup\$
0
\$\begingroup\$

Perl 6, 70 bytes

Whole program that reads the given examples terminated with newline or EOF from STDIN.

# perl6 % <<< '[1, [[2, 3, [[4], 5], 6, [7, 8]], 9, [10, [[[11]]]], 12, 13], 14]'

my $i;dd [max] lines.comb.map:{$i++when '[';$i--when ']'}

lines takes text line by line from STDIN and returns a list of Str. Calling .comb on a list will convert that into a Str and then split it into graphenes. Any block in Perl 6 is born with one positional argument that ends up in $_. map called with any callable expect it to have one positional argument. What is nice because when ']' is short for when $_ ~~ ']'. The returned list is fed to [max] and reduced to it's biggest value.

\$\endgroup\$
0
\$\begingroup\$

C#, 99 Bytes

int f(string s){int m=0,w=0;foreach(char c in s){if(c=='[')w++;if(c==']')w--;if(w>m)m=w;}return m;}
\$\endgroup\$
1
  • \$\begingroup\$ Does int m=w=0 work in C# ? \$\endgroup\$
    – Cyoce
    Oct 7, 2016 at 0:40
0
\$\begingroup\$

Axiom 106 bytes

RL(a:Union(List(Any),Any)):INT==(a case List(Any)=>(g:List(Any):=a;r:=1;for i in g repeat r:=r+RL(i);r);0)

ungolf

RL(a:Union(List(Any),Any)):INT==
  a case List(Any)=>
          g:List(Any):=a
          r:=1
          for i in g repeat r:=r+RL(i)
          r
  0

results

(3) -> for i in[[1],[1,2,3], [[1,2,3]], [3,[3,[3],3],3]]repeat output[i, RL(i)]
   [[1],1]
   [[1,2,3],1]
   [[[1,2,3]],2]
   [[3,[3,[3],3],3],3]
\$\endgroup\$
0
\$\begingroup\$

C++14, 72 70 67 bytes

-2 bytes for removing the curly brackets at for and -3 bytes for replacing m=l>m?l:m with m+=l>m.

As unnamed lambda returning via reference parameter:

[](auto&s,int&m){int l=m=0;for(auto c:s)l+=(c==91)-(c==93),m+=l>m;}

s can be std::string or char[]

Ungolfed and usage:

#include<string>
#include<iostream>

auto f=
[](auto&s,int&m){
 int l=m=0;
 for(auto c:s)
  l+=(c==91)-(c==93),
  m+=l>m;     
}
;


int main(){
 std::string S[] = {
  "[1]",
  "[1, 2, 3]",
  "[[1, 2, 3]]",
  "[3, [3, [3], 3], 3]",
  "[[[[1], 2], [3, [4]]]]",
  "[1, [[3]], [5, 6], [[[[8]]]], 1]",
  "[1, [[2, 3, [[4], 5], 6, [7, 8]], 9, [10, [[[11]]]], 12, 13], 14]",
  "[[[[[[[3]]]]]]]"
 };

 for (auto s:S){
  int m;
  g(s,m);
  std::cout << m << std::endl;
 }

}
\$\endgroup\$
0
\$\begingroup\$

Clojure, 57 bytes

(fn[s](apply max(reductions +(map #({\[ 1 \] -1}% 0)s))))

Also based on parsing a string:

(f "[3, [3, [3], 3], 3]")
\$\endgroup\$
0
\$\begingroup\$

R: 53 bytes.

d=function(L) ifelse(is.list(L),max(sapply(L,d))+1,0)

Recursive function, it adds one point everytime it finds an inner list. It chooses only the element with the maximum depth...

\$\endgroup\$
0
\$\begingroup\$

Scala, 56 bytes

val f:Any=>Int={case l:Seq[_]=>l.map(f).max+1 case _=>0}

Make sure the inputs are Seqs and not Lists (I used Seq to save a byte).

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Pip, 14 bytes

{a*0&1+MX:fMa}

Try it online!

Explanation

A recursive function that takes the list as its argument.

{            }  Anonymous function:
 a               The first argument
  *0             Times 0
                 (This gives 0 if the argument is a number, or a [possibly nested]
                  list of 0s if the argument is a list)
    &            Logical AND (short-circuiting)
                 (Since 0 is falsey and non-empty lists are truthy in Pip, this
                  halts the recursion and returns 0 if we've reached the inside of
                  the innermost lists; otherwise...)
          fMa    Map this function recursively to each element of a
       MX:       Get the max (: is a no-op to manipulate operator precedence)
     1+          Add 1
\$\endgroup\$
0
\$\begingroup\$

C (gcc), 62 bytes

n;m;f(char* s){for(;*s;s++)*s^91?*s^93?0:n--:++n>m?m=n:0;m=m;}

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Nekomata, 2 bytes

ˡ∑

Attempt This Online!

ˡ∑
ˡ   Loop until failure and return the number of iterations:
 ∑      Sum
\$\endgroup\$
1
2

Your Answer

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

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