# Determine the depth of an array

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, [, 5], 6, [7, 8]], 9, [10, [[]]], 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, 2, 3]                                                         -> 1
[[1, 2, 3]]                                                       -> 2
[3, [3, , 3], 3]                                               -> 3
[[[, 2], [3, ]]]                                            -> 4
[1, [], [5, 6], [[[]]], 1]                                  -> 5
[1, [[2, 3, [, 5], 6, [7, 8]], 9, [10, [[]]], 12, 13], 14] -> 6
[[[[[[]]]]]]                                                   -> 7

• After discussion in chat I've decided to allow length built-ins, because some languages require them to iterate over an array cleanly. Feb 8, 2016 at 23:09
• Just for general education: ≡ is APL's built-in primitive for exactly this.
Feb 9, 2016 at 16:09
• @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? Feb 10, 2016 at 5:21
• @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. Feb 10, 2016 at 10:39
• @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? Feb 10, 2016 at 22:02

# 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, [, 5], 6, [7, 8]], 9, [10, [[]]], 12, 13], 14]]
=> 6


# 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.

# 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.

# 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],]])
=> 3


• Welcome to the site! Sep 7, 2017 at 17:02

# 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.

# Jelly, 3 bytes

ẎƬL


Try it online!

This is a translation of JohnE's K answer.

Ẏ is the "tighten" command. When applied to a list, it "unwraps" each list in it, essentially reducing the depth by one. Ƭ repeated applies Ẏ to the input until it reaches a fixed point. We then count the number of iterations with L.

# 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, [, 5], 6, [7, 8]], 9, [10, [[]]], 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)
$$$$

• 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. Jul 31, 2020 at 2:40
• Didn't realize that wasn't allowed, updated to reflect that. Jul 31, 2020 at 4:15
• You can save one byte by replacing arg with (...) Aug 21, 2020 at 16:50
• 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? Aug 22, 2020 at 4:42

# 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.

# 05AB1E, 7 bytes

dΔ€}N>


Explanation:

d     # Convert each integer in the (implicit) input-list to 1 (with a >=0 check)
Δ    # Loop until the result no longer changes:
€   #  Map over each inner item:
  #   Pop and dump its contents to the stack
}N   # After the loop, push the last 0-based index
>  # Increase it by 1 to make it a 1-based index
# (which is output implicitly as result)


The €$$$$ basically flattens a list one level down, but will push its items in reversed order. E.g. list [[1,[2,3],4]] will become [4,[2,3],1]. In addition, it will do the same for multi-digit numbers, hence the need for the leading d to convert every integer to a single digit first. E.g. list [12,345,67] will become [2,1,5,4,3,7,6].

# Whython, 28 bytes

f=lambda l:1+max(map(f,l))?0


Attempt This Online!

### Explanation

The depth of a list is the maximum depth of its elements; the depth of an integer is 0.

f=lambda l:1+max(map(f,l))?0
f=                            # Define f as
lambda l:                   # a function of one argument, l:
map(f,l)     #  Call f recursively on each element of l
max(        )    #  Take the maximum
#  If l is an integer, map raises an exception
?0  #  Catch that exception and return 0


# 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.

• 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! Feb 1, 2022 at 18:45
• (Also, for future reference, there's a type? function that is a little shorter than using type with e.) Feb 1, 2022 at 18:46
• @DLosc i was skeptical about whether errors halted execution because i never really got where the error were coming from. anyway, thanks. Feb 2, 2022 at 1:46

# Brachylog, 8 bytes

>0|↰ᵐ⌉+₁


Try it online!

 0|         Output 0
>           if the input is a positive integer;
|↰ᵐ       else map this over the elements of the input
⌉      the largest result.


## Perl 6, 70 bytes

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

# perl6 % <<< '[1, [[2, 3, [, 5], 6, [7, 8]], 9, [10, [[]]], 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.

## 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;}

• Does int m=w=0 work in C# ? Oct 7, 2016 at 0:40

# 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,2,3], [[1,2,3]], [3,[3,,3],3]]repeat output[i, RL(i)]
[,1]
[[1,2,3],1]
[[[1,2,3]],2]
[[3,[3,,3],3],3]


# C++14, 7270 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, 2, 3]",
"[[1, 2, 3]]",
"[3, [3, , 3], 3]",
"[[[, 2], [3, ]]]",
"[1, [], [5, 6], [[[]]], 1]",
"[1, [[2, 3, [, 5], 6, [7, 8]], 9, [10, [[]]], 12, 13], 14]",
"[[[[[[]]]]]]"
};

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

}


Clojure, 57 bytes

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


Also based on parsing a string:

(f "[3, [3, , 3], 3]")


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...

# 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!

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

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