Wizard
(Benjamin)
1
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
tera
2
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
}
Wizard
(Benjamin)
3
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
tera
4
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.
Wizard
(Benjamin)
5
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
cukr
6
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