To start, you're using the ternary operator (?:
) in quite a complex situation. If you first modify your code to use an if
statement instead, the compiler error that was appearing in the call to outputArray.append(data)
- Generic parameter 'Element' could not be inferred
- appears in a much more sensible place (namely, the line of the if statement).

That error is relatively easy to solve - simply replace Array
with Array<Any>
, giving us this:
func flatten(input: [Any]) -> [Any] {
var outputArray = [Any]()
for i in 0..<input.count {
let data = input[i]
if data is Array<Any> {
outputArray += flatten(input: [data])
} else {
outputArray.append(data)
}
}
return outputArray
}
At this point, the original problem still occurs, because the value that's passed in to flatten(input:)
is [data]
. We know, due to the fact that the if
condition was true
, that data
is really of type Array<Any>
, and so the value [data]
must be an Array<Array<Any>>
. Instead, we want to pass in data
, which is already an array.
You say that the reason you wrote [data]
is because the argument has to be an array, and so you were "forced to" by the compiler. In fact, the only thing the compiler is forcing you to do is pass in an argument whose type is declared as Array<Any>
. We've made sure that data
is an array using the if
statement, but data
is still declared as an Any
(because it was an element of input
, which is an Array<Any>
), so the compiler has no idea that it's really an array.
The solution is - instead of using if data is Array<Any>
to determine momentarily whether data
is an array but immediately throw that information away - to convert data
to an Array<Any>
.
The new if
statement becomes if let dataArray = data as? Array<Any>
. The statement data as? Array<Any>
attempts to convert data
to an array, returning a value of type Array<Any>
if successful or nil
otherwise. Then the if let dataArray = ...
statement stores the value in dataArray
and returns true
if given a non-nil
value, or returns false
if given a nil
value (this is called conditional binding).
By doing that, in the true
case of the if
statement we have access to a value dataArray
that is of type Array<Any>
- unlike data
, which is only declared as Any
. Then dataArray
can be passed in to flatten(input:)
, and won't be nested inside another Array
.
func flatten(input: [Any]) -> [Any] {
var outputArray = [Any]()
for i in 0..<input.count {
let data = input[i]
if let dataArray = data as? Array<Any> {
outputArray += flatten(input: dataArray)
} else {
outputArray.append(data)
}
}
return outputArray
}
A couple of other notes:
Array<Any>
is of course equivalent to [Any]
, so the if
statement could be written with that (generally preferred) syntax, like so:
if let dataArray = data as? [Any] {
outputArray += flatten(input: dataArray)
}
Also, there's no need to go through the whole for i in 0..<input.count { let data = input[i] ...
ordeal if you just iterate over the array instead, like so:
func flatten(input: [Any]) -> [Any] {
var outputArray = [Any]()
for data in input {
if let dataArray = data as? [Any] {
outputArray += flatten(input: dataArray)
} else {
outputArray.append(data)
}
}
return outputArray
}