Transform nested array metatype, where nested array has arbitrary depth

Hi,
I would like to transform the metatype of any given nested array of arbitrary depth to the metatype of another nested array.

What I mean with that is the following:
I have a class Wrapper and I have nested arrays of arbitrary depth of any type. Let's say the nested array's innermost type is SomeClass, i.e. metatypes are [SomeClass].self, [[SomeClass]].self, [[[SomeClass]]].self, ... . I now want to take those metatypes and transform them to [Wrapper].self, [[Wrapper]].self, [[[Wrapper]]].self, ...

So essentially I am looking for a function that transforms a given metatype of an array of arbitrary depth like this:
[SomeClass].self -> [Wrapper].self
[[SomeClass]].self -> [[Wrapper]].self
[[[SomeClass]]].self -> [[[Wrapper]]].self
...

Any ideas on how to do that?

My best attempt so far is the following:

// Wrapper class for output
class Wrapper { }

// protocol to identify array and get its Element type
protocol ArrayProtocol {
    static var elementType: Any.Type { get }
}

// get elementType
extension Array: ArrayProtocol {
    static var elementType: Any.Type {
        return Element.self
    }
}

// function that does the actual transform
func transformType(_ input: Any.Type) -> Any.Type {
    func transformTypeRecursive(_ input: Any.Type) -> Any {
        if let arrayType = input as? ArrayProtocol.Type {
            return [transformTypeRecursive(arrayType.elementType)]
        } else {
            return Wrapper()
        }
    }
    return type(of: transformTypeRecursive(input))
}

Unfortunately this does not work as transformType does not maintain the type of the recursively constructed arrays (following is example for nested array of depth 2 with arbitrary class SomeClass):

class SomeClass { }
transformType([[SomeClass]].self) is [[Wrapper]].Type // yields false, but obviously I'd like this to be true

Will this do?

func transform(_ value: Any, using callback: (Any) -> Any) -> Any {
    if let a = value as? Array<Any> {
        return a.map { v in
            transform(v, using: callback)
        }
    } else {
        return callback(value)
    }
}

transform([[[SomeClass()]]]) { value in
    _ x = value as! SomeClass
    // ...
    let wrapper = Wrapper(...)
    return wrapper
}

Thanks for the reply. Unfortunately your code also removes the underlying specific type information due to working with Any. Or am I missing something?

Running yields

let transformedArray = transform([[[SomeClass()]]]) { value in
    return Wrapper()
}
    
type(of: transformedArray) is [[[Wrapper]]].Type // false, same problem as with my code

Why is that important to you, practically speaking?

One obvious way is this:

func transform(_ value: [SomeClass]) -> [Wrapper] {
    value.map { Wrapper($0) }
}
func transform(_ value: [[SomeClass]]) -> [[Wrapper]] {
    value.map { $0.map { Wrapper($0) }}
}
func transform(_ value: [[[SomeClass]]]) -> [[[Wrapper]]] {
    value.map { $0.map { $0.map { Wrapper($0) }}}
}

up until a certain nesting level.

Why is that important to you, practically speaking?

I would like to use the output type with Codable, specifically use the type to specify the type for KeyedDecodingContainer's

func decode<T>(T.Type, forKey: KeyedDecodingContainer<K>.Key) throws -> T

If there's only a small number of leaf classes and you can write extension Leaf: Foo {} for each of them:

class Wrapper { }
protocol Foo {
    associatedtype Converted
}
extension Foo {
    typealias Converted = Wrapper
}
extension Array: Foo where Element: Foo {
    typealias Converted = [Element.Converted] 
}
class SomeClass: Foo {}

func transform<T: Foo>(_ type: T.Type) -> T.Converted.Type {
    T.Converted.self
}

print([SomeClass].Converted.self) // Array<Wrapper>
print(transform([SomeClass].self)) // Array<Wrapper>
print(transform([[SomeClass]].self)) // Array<Array<Wrapper>>
print(transform([[[SomeClass]]].self)) // Array<Array<Array<Wrapper>>>
print(transform([[[[SomeClass]]]].self)) // Array<Array<Array<Array<Wrapper>>>>
1 Like