How to Unwrap Arbitrarily Deeply Nested Optionals in Swift

Unwrap/coalesce a multi-level optional

This code does what you ask for. The drawback is that you need to implement the Unwrappable protocol in every type you want to put inside optionals. Maybe Sourcery can help with that.

protocol Unwrappable {
associatedtype T

func unwrap(_ default: T) -> T
}

extension Optional {}

extension Optional: Unwrappable where Wrapped: Unwrappable {
typealias T = Wrapped.T

func unwrap(_ defaultValue: T) -> T {
if let value = self {
return value.unwrap(defaultValue)
}
return defaultValue
}
}

extension Int: Unwrappable {
typealias T = Int

func unwrap(_ default: Int) -> Int {
return self
}
}

let nestedOptionalValue: Int??? = 6
let nestedOptionalNil: Int??? = nil
let optionalValue: Int? = 6
let optionalNil: Int? = nil
print(nestedOptionalValue.unwrap(0)) // prints 6
print(nestedOptionalNil.unwrap(0)) // prints 0
print(optionalValue.unwrap(0)) // prints 6
print(optionalNil.unwrap(0)) // prints 0

The trick is that the Unwrappable protocol marks types that can be eventually unwrapped (as in after 0, 1 or more unwraps) to a certain type.

The difficulty in this problem comes from the fact that in an extension to Optional, you can get the Wrapped type, but if Wrapped is optional again, you can't access Wrapped.Wrapped (in other words, Swift doesn't support Higher Kinded Types).

Another approach, would be to try to add an extension to Optional where Wrapped == Optional. But again you'd need Higher Kinded Types support to access the Wrapped.Wrapped type. If we try to extend Optional where Wrapped == Optional<T>, we also fail, because we cant extend Optional generically over T.

Can you safely unwrap nested optionals in swift in one line?

You could do it like so:

if let title = view.annotation?.title as? String {

}

view.annotation?.title is a double optional string: String?? since both the property annotation of an MKAnnotationView, and its own property title, are optional.


You could also use the guard statement like so:

guard let title = view.annotation?.title as? String else {
return
}
//use title in the rest of the scope

you could also use a switch statement :

switch title {
case .some(.some(let t)):
//use the title here
print(t)
default:
break
}

How to unwrap double optionals?

Given a double optional such as this doubly wrapped String:

let a: String?? = "hello"
print(a as Any) // "Optional(Optional("hello"))\n"

@Leo, showed that you could use optional binding twice:

if let temp = a, let value = temp {
print(value) // "hello\n"
}

or force unwrap twice:

print(value!!)  // don't do this - you're just asking for a crash

Here are 5 more methods you can use to safely unwrap a double optional:

Method 1:

You can also use pattern matching:

if case let value?? = a {
print(value) // "hello\n"
}

As @netigger noted in their answer, this can also be written as:

if case .some(.some(let value)) = a {
print(value) // "hello\n"
}

which while less concise might be a bit easier to read.


Method 2:

Alternatively, you can use the nil coalescing operator ?? twice:

print((a ?? "") ?? "")  // "hello\n"

Note: Unlike the other methods presented here, this will always produce a value. "" (empty String) is used if either of the optionals is nil.


Method 3:

Or you can use the nil coalescing operator ?? with optional binding:

if let value = a ?? nil {
print(value) // "hello\n"
}

How does this work?

With a doubly wrapped optional, the value held by the variable could be one of 3 things: Optional(Optional("some string")), Optional(nil) if the inner optional is nil, or nil if the outer optional is nil. So a ?? nil unwraps the outer optional. If the outer optional is nil, then ?? replaces it with the default value of nil. If a is Optional(nil), then ?? will unwrap the outer optional leaving nil. At this point you will have a String? that is nil if either the inner or outer optional is nil. If there is a String inside, you get Optional("some string").

Finally, the optional binding (if let) unwraps Optional("some string") to get "some string" or the optional binding fails if either of the optionals is nil and skips the block.


Method 4:

Also, you can use flatMap with optional binding:

if let value = a.flatMap({ $0 }) {
print(value) // "hello\n"
}

Method 5:

Conditionally cast the value to the type. Surprisingly, this will remove all levels of optionals:

let a: String?? = "hello"
let b: String??????? = "bye"

if let value = a as? String {
print(value) // "hello\n"
}

print(b as Any) // "Optional(Optional(Optional(Optional(Optional(Optional(Optional("bye")))))))\n"

if let value = b as? String {
print(value) // "bye\n"
}

Check array of optionals for nil

There are two contains() functions:

func contains<S : SequenceType where S.Generator.Element : Equatable>(seq: S, x: S.Generator.Element) -> Bool
func contains<S : SequenceType, L : BooleanType>(seq: S, predicate: @noescape (S.Generator.Element) -> L) -> Bool

The first one takes an element as the second argument, but
requires that the elements conform to the Equatable
protocol, which is not the case for optionals.

The second one takes a predicate as second argument, and that
can be used here:

let optArray : [Int?] = [1, nil, 2]
if contains(optArray, { $0 == nil } ) {

}

But any solution must traverse the array until a nil element is
found, so the above solution may be better readable but is not
necessarily more efficient than a for-loop.

Flatten nested tuple type and preserve order

UPDATE for TS4.5:

Now that TypeScript supports recursive conditional types with tail recursion elimination, Flatten<T> can be written directly and for arbitrarily long arrays as

type Flatten<T extends readonly any[], A extends readonly any[] = []> =
T extends [infer F, ...infer R] ?
Flatten<R, F extends readonly any[] ? [...A, ...F] : [...A, F]> :
A

type InputTuple = [A, B, [C, D], [A, B, D], [A, B, C, D, B], A];
type FlattenedTuple = Flatten<InputTuple>;
// type FlattenedTuple = [A, B, C, D, A, B, D, A, B, C, D, B, A]

In fact, you can even get arbitrarily deep flattening by applying another layer of recursion:

type FlattenDeep<T extends readonly any[], A extends readonly any[] = []> =
T extends [infer F, ...infer R] ?
FlattenDeep<R, F extends readonly any[] ? [...A, ...FlattenDeep<F>] : [...A, F]> :
A

type InputDeepTuple = [A, B, [C, D], [A, B, D], [A, B, [[C], D], [B]], A];
type FlattenedDeepTuple = FlattenDeep<InputTuple>;
// type FlattenedDeepTuple = [A, B, C, D, A, B, D, A, B, C, D, B, A]

Playground link to code


answer for TS4.0

TS4.0 will introduce variadic tuple types in which concatenation of a fixed number of tuples A, B, C, is as simple as using [...A, ...B, ...C]. That means Flatten<T> can be implemented something like this:

type ConcatX<T extends readonly (readonly any[])[]> = [
...T[0], ...T[1], ...T[2], ...T[3], ...T[4],
...T[5], ...T[6], ...T[7], ...T[8], ...T[9],
...T[10], ...T[11], ...T[12], ...T[13], ...T[14],
...T[15], ...T[16], ...T[17], ...T[18], ...T[19]
];
type Flatten<T extends readonly any[]> =
ConcatX<[...{ [K in keyof T]: T[K] extends any[] ? T[K] : [T[K]] }, ...[][]]>

type InputTuple = [A, B, [C, D], [A, B, D], [A, B, C, D, B], A];
type FlattenedTuple = Flatten<InputTuple>;
// type FlattenedTuple = [A, B, C, D, A, B, D, A, B, C, D, B, A]

It still doesn't work for arbitrarily long tuples (or for edge cases such as open-ended tuples), but it's much less crazy than it was before.

Playground link


PRE-TS4.0 answer:

The TypeScript type system isn't really geared toward letting you do this. The most obvious implementation of something like Flatten would be a recursive conditional type; this is not currently supported (see microsoft/TypeScript#26980). You can do it, but there's no guarantee that it will keep working in future versions of TypeScript. And even if you get a working version, it's really easy to make it so that it blows out the TypeScript compiler, causing exceptionally long compile times and even compiler hangs and crashes. The first version of Flatten that I wrote as a test had this problem, taking forever to work on even an output tuple of length 7, and occasionally reporting type instantiation depth errors.

I think the canonical GitHub issue for this feature might be microsoft/TypeScript#5453, a proposal to support variadic (arbitrary length) kinds (basically "types of types"). Right now the only officially-supported way to manipulate tuples in a variadic way is to prepend a fixed number of types onto the beginning, by using tuples in rest/spread positions.


So the "official" answer is something like "you can't or shouldn't do this", but that doesn't stop people from doing it. There's even a library called ts-toolbelt which does all kinds of fun recursive things under the covers to get more arbitrary tuple manipulation. I think the author of this library has really tried to make sure that the compiler performance doesn't suffer, so if I were going to really use anything, I'd probably use that library rather than write it myself. One of the tools on that belt is called Flatten<L> which seems to do what you want. But even this library is still not officially supported.


Still I couldn't resist writing my own version of Flatten, to give you some idea of how hairy it is. It seems to perform well enough. I've limited it to concatenating only up to about 7 tuples, and the total length of the flattened output can't exceed about 30 elements. It uses both iterative and recursive conditional types, the latter of which is unsupported. A particularly clever person might come up with a way to do it completely iteratively, but I am either not that person or it would take me too long to become that person. Okay, enough preamble, here it is:

/* 
codegen
var N = 30;
var range = n => (new Array(n)).fill(0).map((_,i)=>i);
var s = [];
s.push("type Add = ["+range(N).map(i => "["+range(N-i).map(j => i+j+"").join(",")+"]").join(",")+"];")
s.push("type Sub = ["+range(N).map(i => "["+range(i+1).map(j => i-j+"").join(",")+"]").join(",")+"];")
s.push("type Tup = ["+range(N).map(i => "["+range(i).map(_=>"0").join(",")+"]").join(",")+"];")
console.log(s.join("\n"))
*/
type Add = [[0, 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], [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], [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], [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], [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], [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], [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [21, 22, 23, 24, 25, 26, 27, 28, 29], [22, 23, 24, 25, 26, 27, 28, 29], [23, 24, 25, 26, 27, 28, 29], [24, 25, 26, 27, 28, 29], [25, 26, 27, 28, 29], [26, 27, 28, 29], [27, 28, 29], [28, 29], [29]];
type Sub = [[0], [1, 0], [2, 1, 0], [3, 2, 1, 0], [4, 3, 2, 1, 0], [5, 4, 3, 2, 1, 0], [6, 5, 4, 3, 2, 1, 0], [7, 6, 5, 4, 3, 2, 1, 0], [8, 7, 6, 5, 4, 3, 2, 1, 0], [9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]];
type Tup = [[], [0], [0, 0], [0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];

type Arr = readonly any[];

type Tail<T extends Arr> =
((...x: T) => void) extends ((h: infer A, ...t: infer R) => void) ? R : never
type Concat<T extends Arr, U extends Arr> = Tup[Add[T["length"]][U["length"]]] extends infer A ? {
[I in keyof A]: I extends keyof T ? T[I] : U[Sub[Extract<I, keyof Sub>][T["length"]]]
} : never

// in TS4.0, Tail and Concat can be simplified to
// type Tail<T extends Arr> = T extends [infer A, ...infer R] ? R : never;
// type Concat<T extends Arr, U extends Arr> = [...T, ...U];

type Tuplize<T> = { [K in keyof T]: T[K] extends any[] ? T[K] : [T[K]] }

type Flatten<T extends readonly any[], N extends number = 7> =
N extends 0 ? [] :
Tuplize<T> extends infer U ? U extends Arr ?
{ 0: [], 1: Concat<U[0], Extract<Flatten<Tail<U>, Extract<Sub[N][1], number>>, Arr>> }[
U extends [] ? 0 : 1] : never : never;

The first three lines are generated by a small JS script, to produce a bunch of fixed tuples representing the operations of adding and subtracting numbers, and to also get a "blank" tuple of a given length. So Add[3][4] should be 7, Sub[7][3] should be 4, and Tup[3] should be [0,0,0].

From there I define Tail<T>, which takes a tuple like [1,2,3,4] and strips off the first element to produce [2,3,4], and Concat<T, U> which takes two tuples and concatenates them (like Concat<[1,2],[3,4]> should be [1,2,3,4]). The definition of Concat here is purely iterative so it's not illegal yet.

Then I make Tuplize<T>, which just makes sure each element of the tuple T is itself an array. So your [A, B, [C, D], [A]] will become [[A],[B],[C,D],[A]]. This removes the weird edge conditions you get when flattening.

Finally I write the illegal and recursive Flatten<T>. I tried to put some recursion limiting in there; it will only work for lengths up to 7 or so. If you try to increase that just by changing 7 to 25 you're liable to get errors from the compiler. Anyway, the basic approach here is to do a sort of reduce() operation on Tuplize<T>: just Concat the first element of Tuplize<T> on to the Flatten-ed version of the Tail of Tuplized<T>.

Let's see an example:

type InputTuple = [A, B, [C, D], [A, B, D], [A, B, C, D, B], A];
type FlattenedTuple = Flatten<InputTuple>;
// type FlattenedTuple = [A, B, C, D, A, B, D, A, B, C, D, B, A]

Looks good.

There are all sorts of caveats here; it only flattens one level deep (which is what you asked for). It probably doesn't distribute over unions. It might not act the way you want with readonly or optional tuples or arrays. It definitely won't act properly with tuples that are too long or of arbitrary length. It might not act properly if the moon is full or if Jupiter aligns with Mars, etc.

The point of the above code is not for you to drop it into your production system. Please don't do that. It's just to have some fun with the type system and show that this is an open issue.


Okay, hope that helps; good luck!

Playground link to code

One of many recursive calls of a function found the correct result, but it can't tell the others. Is there a better fix than this ugly workaround?

Your first attempt is almost perfect, the only mistake is that you return the result of searching through the first list/tuple at the current depth, regardless of whether the item was found or not. Instead, you need to check for a positive result, and only return if it is one. That way you keep iterating through the current depth until you either find the item or it is not found at all.

So you need to change:

return traverse(S[i], item, indices + [i], atDepth + 1) 

to something like:

t = traverse(S[i], item, indices + [i], atDepth + 1) 
if t != ([], -1):
return t

Full code:

def traverse(S, item, indices=[], atDepth=0):
# If the sequence is empty, return the 'item not found' result
if not S:
return ([], -1)

else:
# For each element in the sequence (breadth-first)
for i in range(len(S)):
# Success condition base case: found the item!
if S[i] == item:
return (indices + [i], atDepth)

# Recursive step (depth-first): enter nested sequence
# and repeat procedure from beginning
elif type(S[i]) in (list, tuple):
t = traverse(S[i], item, indices + [i], atDepth + 1)
if t != ([], -1):
return t

# Fail condition base case: searched the entire length
# and depth of the sequence and didn't find the item, so
# return the 'item not found' result
else:
print("We looked everywhere but didn't find " + str(item) + " in " + str(S) + ".")
return ([], -1)

Output for your two test cases:

>>> traverse(L, 7)
([3, 1, 2, 4], 3)
>>> traverse(L, 9)
We looked everywhere but didn't find 9 in [6, 6.25, 6.5, 6.75, 7].
We looked everywhere but didn't find 9 in (4, 5, [6, 6.25, 6.5, 6.75, 7]).
We looked everywhere but didn't find 9 in [3, (4, 5, [6, 6.25, 6.5, 6.75, 7])].
We looked everywhere but didn't find 9 in [8, ()].
We looked everywhere but didn't find 9 in [[8, ()]].
([5, 0, 0, 0], 3)

Note as pointed out by @FreddyMcloughlan, atDepth is simply the length of the returned list minus 1. So you can remove that parameter from the function call and just use:


def traverse(S, item, indices=[]):
# If the sequence is empty, return the 'item not found' result
if not S:
return ([], -1)

else:
# For each element in the sequence (breadth-first)
for i in range(len(S)):
# Success condition base case: found the item!
if S[i] == item:
return (indices + [i], len(indices))

# Recursive step (depth-first): enter nested sequence
# and repeat procedure from beginning
elif type(S[i]) in (list, tuple):
t = traverse(S[i], item, indices + [i])
if t != ([], -1):
return t

# Fail condition base case: searched the entire length
# and depth of the sequence and didn't find the item, so
# return the 'item not found' result
else:
print("We looked everywhere but didn't find " + str(item) + " in " + str(S) + ".")
return ([], -1)


Related Topics



Leave a reply



Submit