Iterate through Dictionary over all levels - ios

I have a data structure in a Dictionary which looks like this:
- device
- type
- isActive
- Data
- Manufacturer
- Build Date
- Power
...
- Example
Now if I create a For Loop it only shows me the Values of the First level which means
type, isActive, Data, Example
for (key, value) in value {
}
All below Data like Build Date or Power is Missing - how can I irritate through this levels also?

Assuming you have Dictionary with type [String:Any], function to flatten it will be something like:
func flatten(_ obj:[String:Any]) -> [String:Any] {
var result = [String:Any]()
for (key, val) in obj {
if let v = val as? [String:Any] {
result = result.merging(flatten(v), uniquingKeysWith: {
$1
})
}
//I also included initial value of internal dictionary
/if you don't need initial value put next line in 'else'
result[key] = val
}
return result
}
To use that:
let d:[String:Any] = [
"test1":1,
"test2":2,
"test3":[
"test3.1":31,
"test3.2":32
]
]
let res = flatten(d)
print(res)
["test2": 2, "test3.2": 32, "test3.1": 31, "test1": 1, "test3":
["test3.2": 32, "test3.1": 31]]
note: dictionaries are not sorted structures

You are dealing with a Recursion problem here.
You could walk the dictionary level by level:
func walk(dictionary: [String: Any]) {
for (key, value) in dictionary {
print("\(key): \(value)")
if let value = value as? [String: Any] {
walk(dictionary: value)
}
}
}
You can change [String: Any] type with your dictionary type (NSDictionary, etc.)

As Tomas says, this can be solved with recursion. His "walk" function simply prints the values on separate lines, but with no keys, and not formatting. Here is code that logs nested dictionaries using the structure you outlined:
//Define the keys we use in our example
enum keys: String {
case device
case type
case isActive
case Data
case Manufacturer
case BuildDate
case Power
case Example
}
//Create a sample dictionary using those keys and some random values
let dict = [keys.device.rawValue:
[keys.type.rawValue: "appliance",
keys.isActive.rawValue: true,
keys.Data.rawValue:
[keys.Manufacturer.rawValue: "GE",
keys.BuildDate.rawValue: "12/23/96",
keys.Power.rawValue: 23,
],
keys.Example.rawValue: "Foo"
]
]
//Create an extension of Dictionary for dictionaries with String Keys.
extension Dictionary where Key == String {
//Define a function to recursively build a description of our dictionary
private func descriptionOfDict(_ aDict: [String: Any], level: Int = 0) -> String {
var output = ""
var indent: String = ""
if level > 0 {
output += "\n"
for _ in 1...level {
indent += " "
}
}
for (key,value) in aDict {
output += indent + key + ": "
if let subDict = value as? [String: Any] {
//If the value for this key is another dictionary, log that recursively
output += descriptionOfDict(subDict, level: level+1)
} else {
if let aString = value as? String {
output += "\"" + aString + "\"\n"
} else {
output += String(describing: value) + "\n"
}
}
}
return output
}
//Add a description property for dictionaries
var description: String {
return descriptionOfDict(self)
}
}
print(dict.description)
That outputs:
device:
isActive: true
Data:
Manufacturer: "GE"
Power: 23
BuildDate: "12/23/96"
Example: "Foo"
type: "appliance"
Edit:
The above, defining a String property description, changes the output when you print a [String:Any] dictionary. If you don't want that, rename the property description to something else like dictDescription

Related

Get average of Integer values by comparing 2 different array based on their index

I'm stuck in this problem.
Here is my Dictionary of arrays:
{"Image":["22301657205688/Chin2231657205705u3zK.jpeg","22301657205688/Chin2231657205707k6HN.jpeg","22301657205688/Chin2231657205708Ip57.jpeg","22301657205688/Forehead2231657205693CbX9.jpeg","22301657205688/L Cheek2231657205697g8d4.jpeg","22301657205688/Nose22316572057008AGT.jpeg","22301657205688/Nose2231657205702k9OU.jpeg"],"OutputScore":[3,9,9,3,1,3,9],"TotalScore":5.285714285714286}
I need to get the average number of OutputScore for the common Image name like Chin and Nose which are common in an array of Image. How can I filter the common name then compare it with the OutputScore indexes and get the average for the same names?
For Example There is 2 Nose Image name at index 5,6 and I need their average score from OutputScore value 3,9 at the same index.
Please help. Thanks.
Let's start with parsing the JSON, and extract the values:
let jsonStr = """
{
"Image": [
"22301657205688/Chin2231657205705u3zK.jpeg",
"22301657205688/Chin2231657205707k6HN.jpeg",
"22301657205688/Chin2231657205708Ip57.jpeg",
"22301657205688/Forehead2231657205693CbX9.jpeg",
"22301657205688/L Cheek2231657205697g8d4.jpeg",
"22301657205688/Nose22316572057008AGT.jpeg",
"22301657205688/Nose2231657205702k9OU.jpeg"
],
"OutputScore": [
3,
9,
9,
3,
1,
3,
9
],
"TotalScore": 5.285714285714286
}
"""
let jsonDict = try! JSONSerialization.jsonObject(with: Data(jsonStr.utf8), options: []) as! [String: Any]
let images = jsonDict["Image"] as! [String]
let scores = jsonDict["OutputScore"] as! [Int]
You need a method to extract the "name" from that partial URL. Here's an attempt to do so. Your full needs aren't clear enough, but it does the trick for your sample.
func extractPart(from: String) -> String? {
let regex = try! NSRegularExpression(pattern: "\\d+\\/([A-Za-z ]+)", options: [])
guard let firstMatch = regex.firstMatch(in: from, options: [], range: NSRange(location: 0, length: from.utf16.count)) else { return nil }
let partNSRange = firstMatch.range(at: 1)
guard let partRange = Range(partNSRange, in: from) else { return nil }
let part = from[partRange]
return String(part)
}
We need to "link" images[0] & scores[0], images[1] & scores[1], ... images[n] & scores[n]
To do so, we can use zip():
let zip = zip(images, scores)
Now, let's regroup the zip values which have the same part name:
We can use Dictionary(grouping:by:) in order to group the values, transforming it into a Dictionary where keys are the part name, and values the zip couples:
let partDict: [String : [(String, Int)]] = Dictionary(grouping: zip) { anCoupleElement in
guard let name = extractPart(from: anCoupleElement.0) else {
print("Couldn't extract part name from \(anCoupleElement.0)")
return "Unknown Key"
}
return name
}
print(partDict)
We can use reduce(into:_:) in order to group the values, transforming it into a Dictionary where keys are the part name, and values the zip couples
let reduced = zip.reduce(into: [String: [(String, Int)]]()) { partialResult, current in
guard let name = extractPart(from: current.0) else {
print("Couldn't extract part name from \(current.0)")
return
}
partialResult[name, default: []] += [current]
}
print(reduced)
Then, we can calculate the average.
I choose an iteration, since it's not clear if you have the "Chin", and you need to search the dictionary for it or not. I used a for loop to show them all:
for (aPartName, values) in partDict { //or for (aPartName, values) in reduced
let average = Float(values.reduce(0) { $0 + $1.1 }) / Float(values.count)
print("For: \(aPartName), average: \(average)")
print("With values:")
values.forEach {
print("\t\($0.0) - \($0.1)")
}
}
Final Output:
For: Forehead, average: 3.0
With values:
22301657205688/Forehead2231657205693CbX9.jpeg - 3
For: Nose, average: 6.0
With values:
22301657205688/Nose22316572057008AGT.jpeg - 3
22301657205688/Nose2231657205702k9OU.jpeg - 9
For: L Cheek, average: 1.0
With values:
22301657205688/L Cheek2231657205697g8d4.jpeg - 1
For: Chin, average: 7.0
With values:
22301657205688/Chin2231657205705u3zK.jpeg - 3
22301657205688/Chin2231657205707k6HN.jpeg - 9
22301657205688/Chin2231657205708Ip57.jpeg - 9
If I understood correctly, once you convert that response to an object you would need to process it. You could iterate all elements of the array "Image" and check if the element contains the feature you look for, if they do, add the value in that same index of OutputScore, finally average that.
Assuming Images and OutputScore have the same size, and you have parsed them already, the code would look like:
func getAverageFor(feature: String) -> Double {
var elementsSum: Int = 0
var elementsSeen: Int = 0
for idx in 0 ..< ImageNames.count {
if ImageNames[idx].contains(feature) {
elementsSum += OutputScore[idx]
elementsSeen += 1
}
}
if elementsSeen > 0 {
return Double(elementsSum) / Double(elementsSeen)
}
return 0 // I assume that non present features should be 0
}
I'm assuming that you will be converting your response to Model object. So, your model looks something like this
struct ImageParts: Codable {
let image: [String]
let outputScore: [Int]
let totalScore: Double
enum CodingKeys: String, CodingKey {
case image = "Image"
case outputScore = "OutputScore"
case totalScore = "TotalScore"
}
}
Once you have your model object you can use below function to get the all averages mapped
func getAvgerageMapped(from model: ImageParts, for parts: [String]) -> [[String: Double]]{
return parts.map { key -> [String: Double] in
var op = [Int]()
model.image.enumerated().reduce(op) { partialResult, sequence in
if sequence.element.contains(key) {
op.append(model.outputScore[sequence.offset])
}
return op
}
var avg = 0.0
op.reduce(into: avg) { _, i in
avg += Double(i)
}
avg = avg/Double(op.count)
return [key:avg]
}
}
let averages = getAvgerageMapped(from: model, for: ["Chin", "Forehead", "L Cheek", "Nose"])
print(averages) //[["Chin": 7.0], ["Forehead": 3.0], ["L Cheek": 1.0], ["Nose": 6.0]]

Flatten an array of dictionaries to one dictionary

I am having an array of dictionaries with columnId and columnValue as a pair. Now i need to flatten it as columnId as the key and columnValue as the value of it. How is it possible to do with swift higher order functions?
let arrayOfDictionaries = [["columnId": 123, "columnValue": "sample text"], ["columnId": 124, "columnValue": 9977332]]
//The end result should be:
flattenedDictionary: [String: Any] = ["123": "sample text", "124": 9977332]
Note: Result dictionary will be in the form of [String: Any]
This would work:
func flatten(_ pairs: [[String: Any]]) -> [String: Any] {
pairs.reduce(into: [String: Any]()) {
if let id = $1["columnId"] as? Int, let value = $1["columnValue"] {
$0["\(id)"] = value
}
}
}
You can do this in two steps;
Convert your input array into a sequence of key-value pairs using compactMap
Convert the sequence back into a dictionary using Dictionary(uniqueKeysWithValues:)
let arrayOfDictionaries = [["columnId": 123, "columnValue": "sample text"], ["columnId": 124, "columnValue": 9977332]]
let tupleArray:[(String,Any)] = arrayOfDictionaries.compactMap { dict in
guard let id = dict["columnId"], let value = dict["columnValue"] else {
return nil
}
return ("\(id)",value)
}
let flattenedDictionary: [String: Any] = Dictionary(uniqueKeysWithValues: tupleArray)
Note that this code will throw an exception if there are duplicate keys. You should either take steps to ensure the columnId values are unique or use Dictionary(keysAndValues:, uniquingKeysWith:) to resolve id clashes.

How to check String value for nil before converting it to Double

Currently I'm learning by Apple's Swift tutorials. There is a task where I need to attempt to convert String into a number.
Create a variable total2 of type Double set to 0. Loop through the collection again, adding up all the integers and doubles. For each string that you come across during the loop, attempt to convert the string into a number, and add that value to the total. Ignore booleans. Print the total.
I am trying to use optional binding
else if let someItem = value as? String {
if let someItemUnwrapped = someItem {
total2 += Double(someItemUnwrapped) // TODO (NOT WORKING)
}
}
to check if I can convert String into Double, but I receive this error:
initializer for conditional binding must have Optional type, not 'String'
Here's my code:
var dictionary: [String: Any] = ["Apples": 5, "LightIsOn": false, "Score": 16.3, "IsBusy": true, "Book": "Hamlet"]
for (key, value) in dictionary {
print("Key: \(key); Value: \(value)")
}
var total2: Double = 0
for (key, value) in dictionary {
if let someItem = value as? Int {
total2 += Double(someItem)
} else if let someItem = value as? Double {
total += someItem
} else if let someItem = value as? String {
if let someItemUnwrapped = someItem {
total2 += Double(someItemUnwrapped) // TODO (NOT WORKING)
}
}
}
I figured it out.
if let someItemCheck = Double(someItem) {
total += someItemCheck
}
This will be like this:
Double(someItem) - to check if our someItem String value contains numbers like "125" or "753"
If this happens, we assign this value to let constant someItemCheck
And after that, we adding this value to total2

Remove nested key from dictionary

Let's say I have a rather complex dictionary, like this one:
let dict: [String: Any] = [
"countries": [
"japan": [
"capital": [
"name": "tokyo",
"lat": "35.6895",
"lon": "139.6917"
],
"language": "japanese"
]
],
"airports": [
"germany": ["FRA", "MUC", "HAM", "TXL"]
]
]
I can access all fields with if let .. blocks optionally casting to something that I can work with, when reading.
However, I am currently writing unit tests where I need to selectively break dictionaries in multiple ways.
But I don't know how to elegantly remove keys from the dictionary.
For example I want to remove the key "japan" in one test, in the next "lat" should be nil.
Here's my current implementation for removing "lat":
if var countries = dict["countries"] as? [String: Any],
var japan = countries["japan"] as? [String: Any],
var capital = japan["capital"] as? [String: Any]
{
capital.removeValue(forKey: "lat")
japan["capital"] = capital
countries["japan"] = japan
dictWithoutLat["countries"] = countries
}
Surely there must be a more elegant way?
Ideally I'd write a test helper that takes a KVC string and has a signature like:
func dictWithoutKeyPath(_ path: String) -> [String: Any]
In the "lat" case I'd call it with dictWithoutKeyPath("countries.japan.capital.lat").
When working with a subscript, if the subscript is get/set and the variable is mutable, then the entire expression is mutable. However, due to the type cast the expression "loses" the mutability. (It's not an l-value anymore).
The shortest way to solve this is by creating a subscript that is get/set and does the conversion for you.
extension Dictionary {
subscript(jsonDict key: Key) -> [String:Any]? {
get {
return self[key] as? [String:Any]
}
set {
self[key] = newValue as? Value
}
}
}
Now you can write the following:
dict[jsonDict: "countries"]?[jsonDict: "japan"]?[jsonDict: "capital"]?["name"] = "berlin"
We liked this question so much that we decided to make a (public) Swift Talk episode about it: mutating untyped dictionaries
I'd to like to follow up on my previous answer with another solution. This one extends Swift's Dictionary type with a new subscript that takes a key path.
I first introduce a new type named KeyPath to represent a key path. It's not strictly necessary, but it makes working with key paths much easier because it lets us wrap the logic of splitting a key path into its components.
import Foundation
/// Represents a key path.
/// Can be initialized with a string of the form "this.is.a.keypath"
///
/// We can't use Swift's #keyPath syntax because it checks at compilet time
/// if the key path exists.
struct KeyPath {
var elements: [String]
var isEmpty: Bool { return elements.isEmpty }
var count: Int { return elements.count }
var path: String {
return elements.joined(separator: ".")
}
func headAndTail() -> (String, KeyPath)? {
guard !isEmpty else { return nil }
var tail = elements
let head = tail.removeFirst()
return (head, KeyPath(elements: tail))
}
}
extension KeyPath {
init(_ string: String) {
elements = string.components(separatedBy: ".")
}
}
extension KeyPath: ExpressibleByStringLiteral {
init(stringLiteral value: String) {
self.init(value)
}
init(unicodeScalarLiteral value: String) {
self.init(value)
}
init(extendedGraphemeClusterLiteral value: String) {
self.init(value)
}
}
Next I create a dummy protocol named StringProtocol that we later need to constrain our Dictionary extension. Swift 3.0 doesn't yet support extensions on generic types that constrain a generic parameter to a concrete type (such as extension Dictionary where Key == String). Support for this is planned for Swift 4.0, but until then, we need this little workaround:
// We need this because Swift 3.0 doesn't support extension Dictionary where Key == String
protocol StringProtocol {
init(string s: String)
}
extension String: StringProtocol {
init(string s: String) {
self = s
}
}
Now we can write the new subscripts. The implementation for the getter and setter are fairly long, but they should be straightforward: we traverse the key path from beginning to end and then get/set the value at that position:
// We want extension Dictionary where Key == String, but that's not supported yet,
// so work around it with Key: StringProtocol.
extension Dictionary where Key: StringProtocol {
subscript(keyPath keyPath: KeyPath) -> Any? {
get {
guard let (head, remainingKeyPath) = keyPath.headAndTail() else {
return nil
}
let key = Key(string: head)
let value = self[key]
switch remainingKeyPath.isEmpty {
case true:
// Reached the end of the key path
return value
case false:
// Key path has a tail we need to traverse
switch value {
case let nestedDict as [Key: Any]:
// Next nest level is a dictionary
return nestedDict[keyPath: remainingKeyPath]
default:
// Next nest level isn't a dictionary: invalid key path, abort
return nil
}
}
}
set {
guard let (head, remainingKeyPath) = keyPath.headAndTail() else {
return
}
let key = Key(string: head)
// Assign new value if we reached the end of the key path
guard !remainingKeyPath.isEmpty else {
self[key] = newValue as? Value
return
}
let value = self[key]
switch value {
case var nestedDict as [Key: Any]:
// Key path has a tail we need to traverse
nestedDict[keyPath: remainingKeyPath] = newValue
self[key] = nestedDict as? Value
default:
// Invalid keyPath
return
}
}
}
}
And this is how it looks in use:
var dict: [String: Any] = [
"countries": [
"japan": [
"capital": [
"name": "tokyo",
"lat": "35.6895",
"lon": "139.6917"
],
"language": "japanese"
]
],
"airports": [
"germany": ["FRA", "MUC", "HAM", "TXL"]
]
]
dict[keyPath: "countries.japan"] // ["language": "japanese", "capital": ["lat": "35.6895", "name": "tokyo", "lon": "139.6917"]]
dict[keyPath: "countries.someothercountry"] // nil
dict[keyPath: "countries.japan.capital"] // ["lat": "35.6895", "name": "tokyo", "lon": "139.6917"]
dict[keyPath: "countries.japan.capital.name"] // "tokyo"
dict[keyPath: "countries.japan.capital.name"] = "Edo"
dict[keyPath: "countries.japan.capital.name"] // "Edo"
dict[keyPath: "countries.japan.capital"] // ["lat": "35.6895", "name": "Edo", "lon": "139.6917"]
I really like this solution. It's quite a lot of code, but you only have to write it once and I think it looks very nice in use.
You could construct recursive methods (read/write) which visits your given key path by repeatedly attempting conversions of (sub-)dictionary values to [Key: Any] dictionaries themselves. Moreover, allow public access to these methods via a new subscript.
Note that you might have to explicitly import Foundation for access to the components(separatedBy:) method of String (bridged).
extension Dictionary {
subscript(keyPath keyPath: String) -> Any? {
get {
guard let keyPath = Dictionary.keyPathKeys(forKeyPath: keyPath)
else { return nil }
return getValue(forKeyPath: keyPath)
}
set {
guard let keyPath = Dictionary.keyPathKeys(forKeyPath: keyPath),
let newValue = newValue else { return }
self.setValue(newValue, forKeyPath: keyPath)
}
}
static private func keyPathKeys(forKeyPath: String) -> [Key]? {
let keys = forKeyPath.components(separatedBy: ".")
.reversed().flatMap({ $0 as? Key })
return keys.isEmpty ? nil : keys
}
// recursively (attempt to) access queried subdictionaries
// (keyPath will never be empty here; the explicit unwrapping is safe)
private func getValue(forKeyPath keyPath: [Key]) -> Any? {
guard let value = self[keyPath.last!] else { return nil }
return keyPath.count == 1 ? value : (value as? [Key: Any])
.flatMap { $0.getValue(forKeyPath: Array(keyPath.dropLast())) }
}
// recursively (attempt to) access the queried subdictionaries to
// finally replace the "inner value", given that the key path is valid
private mutating func setValue(_ value: Any, forKeyPath keyPath: [Key]) {
guard self[keyPath.last!] != nil else { return }
if keyPath.count == 1 {
(value as? Value).map { self[keyPath.last!] = $0 }
}
else if var subDict = self[keyPath.last!] as? [Key: Value] {
subDict.setValue(value, forKeyPath: Array(keyPath.dropLast()))
(subDict as? Value).map { self[keyPath.last!] = $0 }
}
}
}
Example setup
// your example dictionary
var dict: [String: Any] = [
"countries": [
"japan": [
"capital": [
"name": "tokyo",
"lat": "35.6895",
"lon": "139.6917"
],
"language": "japanese"
]
],
"airports": [
"germany": ["FRA", "MUC", "HAM", "TXL"]
]
]
Example usage:
// read value for a given key path
let isNil: Any = "nil"
print(dict[keyPath: "countries.japan.capital.name"] ?? isNil) // tokyo
print(dict[keyPath: "airports"] ?? isNil) // ["germany": ["FRA", "MUC", "HAM", "TXL"]]
print(dict[keyPath: "this.is.not.a.valid.key.path"] ?? isNil) // nil
// write value for a given key path
dict[keyPath: "countries.japan.language"] = "nihongo"
print(dict[keyPath: "countries.japan.language"] ?? isNil) // nihongo
dict[keyPath: "airports.germany"] =
(dict[keyPath: "airports.germany"] as? [Any] ?? []) + ["FOO"]
dict[keyPath: "this.is.not.a.valid.key.path"] = "notAdded"
print(dict)
/* [
"countries": [
"japan": [
"capital": [
"name": "tokyo",
"lon": "139.6917",
"lat": "35.6895"
],
"language": "nihongo"
]
],
"airports": [
"germany": ["FRA", "MUC", "HAM", "TXL", "FOO"]
]
] */
Note that if a supplied key path does not exist for an assignment (using setter), this will not result in the construction of the equivalent nested dictionary, but simply result in no mutation of the dictionary.
Interesting question. The problem seems to be that Swift's optional chaining mechanism, which is normally capable of mutating nested dictionaries, trips over the necessary type casts from Any to [String:Any]. So while accessing a nested element becomes only unreadable (because of the typecasts):
// E.g. Accessing countries.japan.capital
((dict["countries"] as? [String:Any])?["japan"] as? [String:Any])?["capital"]
… mutating a nested element doesn't even work:
// Want to mutate countries.japan.capital.name.
// The typecasts destroy the mutating optional chaining.
((((dict["countries"] as? [String:Any])?["japan"] as? [String:Any])?["capital"] as? [String:Any])?["name"] as? String) = "Edo"
// Error: Cannot assign to immutable expression
Possible Solution
The idea is to get rid of the untyped dictionary and convert it into a strongly typed structure where each element has the same type. I admit that this is a heavy-handed solution, but it works quite well in the end.
An enum with associated values would work well for our custom type that replaces the untyped dictionary:
enum KeyValueStore {
case dict([String: KeyValueStore])
case array([KeyValueStore])
case string(String)
// Add more cases for Double, Int, etc.
}
The enum has one case for each expected element type. The three cases cover your example, but it could easily be expanded to cover more types.
Next, we define two subscripts, one for keyed access to a dictionary (with strings) and one for indexed access to an array (with integers). The subscripts check if self is a .dict or .array respectively and if so return the value at the given key/index. They return nil if the type doesn't match, e.g. if you tried to access a key of a .string value. The subscripts also have setters. This is key to make chained mutation work:
extension KeyValueStore {
subscript(_ key: String) -> KeyValueStore? {
// If self is a .dict, return the value at key, otherwise return nil.
get {
switch self {
case .dict(let d):
return d[key]
default:
return nil
}
}
// If self is a .dict, mutate the value at key, otherwise ignore.
set {
switch self {
case .dict(var d):
d[key] = newValue
self = .dict(d)
default:
break
}
}
}
subscript(_ index: Int) -> KeyValueStore? {
// If self is an array, return the element at index, otherwise return nil.
get {
switch self {
case .array(let a):
return a[index]
default:
return nil
}
}
// If self is an array, mutate the element at index, otherwise return nil.
set {
switch self {
case .array(var a):
if let v = newValue {
a[index] = v
} else {
a.remove(at: index)
}
self = .array(a)
default:
break
}
}
}
}
Lastly, we add some convenience initializers for initializing our type with dictionary, array or string literals. These are not strictly necessary, but make working with the type easier:
extension KeyValueStore: ExpressibleByDictionaryLiteral {
init(dictionaryLiteral elements: (String, KeyValueStore)...) {
var dict: [String: KeyValueStore] = [:]
for (key, value) in elements {
dict[key] = value
}
self = .dict(dict)
}
}
extension KeyValueStore: ExpressibleByArrayLiteral {
init(arrayLiteral elements: KeyValueStore...) {
self = .array(elements)
}
}
extension KeyValueStore: ExpressibleByStringLiteral {
init(stringLiteral value: String) {
self = .string(value)
}
init(extendedGraphemeClusterLiteral value: String) {
self = .string(value)
}
init(unicodeScalarLiteral value: String) {
self = .string(value)
}
}
And here's the example:
var keyValueStore: KeyValueStore = [
"countries": [
"japan": [
"capital": [
"name": "tokyo",
"lat": "35.6895",
"lon": "139.6917"
],
"language": "japanese"
]
],
"airports": [
"germany": ["FRA", "MUC", "HAM", "TXL"]
]
]
// Now optional chaining works:
keyValueStore["countries"]?["japan"]?["capital"]?["name"] // .some(.string("tokyo"))
keyValueStore["countries"]?["japan"]?["capital"]?["name"] = "Edo"
keyValueStore["countries"]?["japan"]?["capital"]?["name"] // .some(.string("Edo"))
keyValueStore["airports"]?["germany"]?[1] // .some(.string("MUC"))
keyValueStore["airports"]?["germany"]?[1] = "BER"
keyValueStore["airports"]?["germany"]?[1] // .some(.string("BER"))
// Remove value from array by assigning nil. I'm not sure if this makes sense.
keyValueStore["airports"]?["germany"]?[1] = nil
keyValueStore["airports"]?["germany"] // .some(array([.string("FRA"), .string("HAM"), .string("TXL")]))
Pass your dictionary to this function, It will return you a flat dictionary, without any nested dict incorporated .
//SWIFT 3.0
func concatDict( dict: [String: Any])-> [String: Any]{
var dict = dict
for (parentKey, parentValue) in dict{
if let insideDict = parentValue as? [String: Any]{
let keys = insideDict.keys.map{
return parentKey + $0
}
for (key, value) in zip(keys, insideDict.values) {
dict[key] = value
}
dict[parentKey] = nil
dict = concatDict(dict: dict)
}
}
return dict
}

Swift filter dictionary error: Cannot assign a value of type '[(_, _)]' to a value of type '[_ : _]'

I am trying to filter a dictionary in swift:
var data: [String: String] = [:]
data = data.filter { $0.1 == "Test" }
the filter code above compiles under Swift 2 but yields the following error:
Cannot assign a value of type '[(String, String)]' to a value of type '[String : String]'
is this a bug in the Swift compiler or is this not the right way to filter dictionaries in Swift?
This has been fixed in Swift 4
let data = ["a": 0, "b": 42]
let filtered = data.filter { $0.value > 10 }
print(filtered) // ["b": 42]
In Swift 4, a filtered dictionary returns a dictionary.
Original answer for Swift 2 and 3
The problem is that data is a dictionary but the result of filter is an array, so the error message says that you can't assign the result of the latter to the former.
You could just create a new variable/constant for your resulting array:
let data: [String: String] = [:]
let filtered = data.filter { $0.1 == "Test" }
Here filtered is an array of tuples: [(String, String)].
Once filtered, you can recreate a new dictionary if this is what you need:
var newData = [String:String]()
for result in filtered {
newData[result.0] = result.1
}
If you decide not to use filter you could mutate your original dictionary or a copy of it:
var data = ["a":"Test", "b":"nope"]
for (key, value) in data {
if value != "Test" {
data.removeValueForKey(key)
}
}
print(data) // ["a": "Test"]
Note: in Swift 3, removeValueForKey has been renamed removeValue(forKey:), so in this example it becomes data.removeValue(forKey: key).
data.forEach { if $1 != "Test" { data[$0] = nil } }
Just another approach (a bit simplified) to filter out objects in your dictionary.
Per Apple docs, filter:
Returns an array containing, in order, the elements of the sequence that satisfy the given predicate.
https://developer.apple.com/reference/swift/sequence/1641239-filter
I found myself needing to do what the OP was asking about and ended up writing the following extensions (Swift 3):
extension Dictionary
{
func filteredDictionary(_ isIncluded: (Key, Value) -> Bool) -> Dictionary<Key, Value>
{
return self.filter(isIncluded).toDictionary(byTransforming: { $0 })
}
}
extension Array
{
func toDictionary<H:Hashable, T>(byTransforming transformer: (Element) -> (H, T)) -> Dictionary<H, T>
{
var result = Dictionary<H,T>()
self.forEach({ element in
let (key,value) = transformer(element)
result[key] = value
})
return result
}
}
Usage:
let data = ["a":"yes", "b":"nope", "c":"oui", "d":"nyet"]
let filtered = data.filteredDictionary({ $0.1 >= "o" })
// filtered will be a dictionary containing ["a": "yes", "c": "oui"]
I've found this method to be useful after filtering or applying some other transform that results in an array of dictionary elements:
extension Array {
func dictionary<K: Hashable, V>() -> [K: V] where Element == Dictionary<K, V>.Element {
var dictionary = [K: V]()
for element in self {
dictionary[element.key] = element.value
}
return dictionary
}
}
To use it, just say something like:
dictionary = dictionary.filter{ $0.key == "test" }.dictionary()
The advantage of this method is that no argument of any kind needs to be passed to the dictionary() method. Generic type arguments tell the compiler everything it needs to know.
You can arrange ascending order according to dictionary value using filter
let arrOfDict = [{"ABC":24},{"XYZ":21},{"AAA":23}]
let orderedDict = arrOfDict.filter{$0.value < $1.value}
you will get below output:
[
{ "XYZ": 21 },
{ "AAA": 23 },
{ "ABC": 24 }
]

Resources