Reputation: 520
Here's the code I try to make work
struct A {
var x:Int = 0
}
struct B {
var y:Int = 0
}
var c: [String:Any] = [
"a":[A()],
"b":[B()]
]
for (key, value) in c {
let arr = value as! [Any]
}
It just throws exception. The runtime exception is raised when trying to cast Any to [Any].
The main thing I want to achieve is iterate through the elements of Any, if Any is array. To me it was natural to cast Any to [Any], but for some reason it doesn't work. So how can I do this in obvious thing in swift?
I saw some workarounds to cast Any to [A] or [B], but that's not my case, because the array can contain just an arbitrary struct.
Upvotes: 2
Views: 1982
Reputation: 73176
You can make use of runtime introspection to inspect whether values in your dictionary are of collection type, and if so, iterate over their children (= elements, for array case), and append these to an actual array of Any
, in so letting Swift know some Any
values in your dictionary are actually arrays.
/* Example setup */
struct A {
var x: Int
init(_ x: Int) { self.x = x }
}
struct B {
var y: Int
init(_ y: Int) { self.y = y }
}
var c: [String:Any] = [
"a": [A(1), A(2)],
"b": [B(3)],
"c": "JustAString",
"d": A(0)
]
E.g. as follows
/* runtime introspection to extract array values from dictionary */
var commonAnyArr: [[Any]] = []
for (_, value) in c {
if case let m = Mirror(reflecting: value)
where (m.displayStyle ?? .Struct) == .Collection {
let arr = m.children.map { $0.value }
commonAnyArr.append(arr)
}
}
/* resulting array of any arrs, that Swift now recognize as actual arrays */
commonAnyArr.forEach { print($0) }
/* [B(y: 3)]
[A(x: 1), A(x: 2)] */
commonAnyArr.flatten().forEach { print($0) }
/* B(y: 3)
A(x: 1)
A(x: 2) */
Alternatively, use the runtime introspection to construct a new dictionary, containing only the key-value pairs of c
where the underlying value wrapped by the Any
value is in fact an array (however in the new dictionary explicitly specifying for swift that the values are arrays of Any
).
/* runtime introspection to extract array values from dictionary */
var dictOfAnyArrs: [String: [Any]] = [:]
for (key, value) in c {
if case let m = Mirror(reflecting: value)
where (m.displayStyle ?? .Struct) == .Collection {
let arr = m.children.map { $0.value }
dictOfAnyArrs[key] = arr
}
}
/* "remaining" dictionary keys now only with [Arr] values */
for (key, arr) in dictOfAnyArrs {
for element in arr {
print("Do something with element \(element)")
}
print("---")
}
/* Do something with element B(y: 3)
---
Do something with element A(x: 1)
Do something with element A(x: 2)
--- */
Just note that the above could be considered somewhat "hacky" (in the eyes of Swift and its pride in static typing and runtime safety), and possibly mostly interesting more out of a technical aspect rather than to be used in actual production code (I would personally never allow anything like the above in production of my own). Perhaps if you take a step back and look at how you've reached this issue, you could re-work your code and design to not reach a point where you need to resort to runtime hacks.
Upvotes: 4