Swift loop through array of dictionaries - loops

Why am I getting this error when looping through an array of dictionaries ?
import UIKit
let tableView = UITableView()
func meh() {
let product = buildCells()
for (identifier, nib) in product {
tableView.registerNib(nib, forCellReuseIdentifier: identifier)
}
}
func buildCells() -> [[String: UINib]] {
var collector = [[String: UINib]]()
let identifier = "identifier"
let nib = UINib(nibName: "TableViewCell", bundle: nil)
let asdf = [identifier: nib];
collector.append(asdf)
return collector
}
The forin loop in the meh() method produces the following error:
'Dictionary' is not convertible to '([[String : UINib]], [[String : UINib]])'

We can't iterate through the keys and values of an array of dictionaries. We can iterate through the dictionaries though. And for each dictionary, we can iterate through its keys and values:
let product = buildCells()
for dict in product {
for (identifier, nib) in dict {
tableview.registerNib(nib, forCellReuseIdentifier: identifier)
}
}
But I think the problem is actually more likely the nonsense going on in buildCells(). Why are you returning an array of dictionaries? Do you plan to have duplicate keys? I don't think the tableview will let you register multiple nibs for the same identifier.
Why don't we just return a dictionary?
func buildCells() -> [String: UINib] {
var dict = [String: UINib]()
dict["identifier"] = UINib(nibName: "TableViewCell", bundle: nil)
// rinse & repeat for all of your other ID/nib combos with no duplicate IDs
return dict
}
Now we can iterate over the key/value pairs in the dictionary without the outer loop:
for (identifier, nib) in dict {
tableview.registerNib(nib, forCellReuseIdentifier: identifier)
}

Related

Appending dictionary values to array in Swift

I have a dictionary of type
[String: Object], where MyObject is an array of AnotherObject. I need to have it sorted as I need to fill an UITableView with Keys.countnumber of sections and Object.count number of rows in each section. I put all the keys in an array, but when I try to append the values into another array I get Thread 1: EXC_BAD_ACCESS (code=1, address=0x8).
This is the code I'm using
var dictionary = [String: MyObject]()
var sortedKeys: [String]?
var sortedValues = [MyObject]()
func sortItems() {
self.sortedKeys = self.dictionary.keys.sorted(by: >)
let sortedDict = self.dictionary.sorted(by: {$0.key > $1.key})
for (_, value) in sortedDict {
print(value)
self.sortedValues.append(value)
}
}
In the for loop, when I don't try to append the values to the array, it prints all the sorted values, the problem comes when I want to have them in an Array.
Edit
The dictionary is like this:
struct Object: Decodable {
let elements: [AnotherObject]
}
struct AnotherObject: Decodable {
let time, Id, Status: Int?
let date, startTime, endTime: String?
}
dictionary: [String: Object]
So the keys are numbers (representing days) and every day has an Object with (at least) one anotherObject.
I get the JSON from the API.
Thanks in advance
You should only sort the keys and then use that array to select from the dictionary and append your sortedValues array. I made the sortedKeys into a local variable
func sortItems() {
let sortedKeys = self.dictionary.keys.sorted(by: >)
for key in sortedKeys {
if let obj = dictionary[key] {
self.sortedValues.append(obj)
}
}
}
I don't know if this will make a difference in regard to the crash but another way is to let the function return an array
func sortItems() -> [Object] {
let sortedKeys = self.dictionary.keys.sorted(by: >)
var result: [Object]()
for key in sortedKeys {
if let obj = dictionary[key] {
result.append(obj)
}
}
return result
}
and then call it
self.sortedValues = sortItems()
You don't use sortedKeys at all and the result of sorting a dictionary is an array of tuples so the dictionary enumeration syntax for (key, value) in is wrong
Probably you want this
func sortItems() {
let sortedKeys = self.dictionary.keys.sorted(by: >)
for key in sortedKeys {
let value = self.dictionary[key]!
print(value)
self.sortedValues.append(value)
}
}
Force unwrapping the value is 100% safe as the key clearly exists.

Swift 4: Filtering Array

i am new to swift, currently practicing
here i have a plist file, which has an Array Of Dictionaries, each dictionary has one string, the plist has 3 records, it looks like this
item 0:
kurdi: Googlee
item 1:
kurdi: Yahooe
item 2:
kurdi: Binge
here's a image for the plist;
Screenshot 11:52AM
okay so the point is, when a user searches for oo for example two of the records contain oo, such as google and yahoo, i want to return an array of results,
for that case i used:
let path = Bundle.main.path(forResource:"hello", ofType: "plist")
let plistData = NSArray(contentsOfFile: path!)
let objCArray = NSMutableArray(array: plistData!)
if let swiftArray = objCArray as NSArray as? [String] {
let matchingTerms = swiftArray.filter({
$0.range(of: "oo", options: .caseInsensitive) != nil // here
})
print(matchingTerms)
}
but unfortunately, when i print matchingTerms it returns nil
..
thanks
If you are new to Swift please learn first not to use NSMutable... Foundation collection types in Swift at all. (The type dance NSArray -> NSMutableArray -> NSArray -> Array is awful). Use Swift native types. And instead of NSArray(contentsOfFile use PropertyListSerialization and the URL related API of Bundle.
All exclamation marks are intended as the file is required to exist in the bundle and the structure is well-known.
let url = Bundle.main.url(forResource:"hello", withExtension: "plist")!
let plistData = try! Data(contentsOf: url)
let swiftArray = try! PropertyListSerialization.propertyList(from: plistData, format: nil) as! [[String:String]]
let matchingTerms = swiftArray.filter({ $0["kurdi"]!.range(of: "oo", options: .caseInsensitive) != nil })
print(matchingTerms)
Cast swift array to [[String:Any]] not [String]. And in filter you need to check the value of the key kurdi. Try this.
if let swiftArray = objCArray as? [[String:Any]] {
let matchingTerms = swiftArray.filter { (($0["kurdi"] as? String) ?? "").range(of: "oo", options: .caseInsensitive) != nil }
print(matchingTerms)
}

Sort an array of dictionaries by a certain key value (Swift 3)

I was looking for a way to sort an array of invoice dictionaries based on invoice ID (which is a value of one of the keys in the dictionary). I couldn't find a solution that worked as well as I would've liked so I wrote out a function which works well for me and I thought I'd share it with the community.
It's pretty simple, you just pass in the array, pass in the key to sort it by and it returns the array sorted.
let testArray: Array<Dictionary<String, String>> = sortDictArrayByKey(arr: someUnsortedArray, key: keyToSort)
func sortDictArrayByKey(arr: Array<Dictionary<String, String>>, key: String) -> Array<Dictionary<String, String>>{
var keyArray = Array<String>()
var usedKeys = Array<String>()
for object in arr{
keyArray.append(object[key]!)
}
keyArray.sort(by: <)
var newArray = Array<Dictionary<String, String>>()
for keyVal in keyArray{
// Check if we've already seen this entry, if so, skip it
if usedKeys.contains(keyVal){
continue
}
usedKeys.append(keyVal)
// Check for duplicate entries
var tempArray = Array<Dictionary<String, String>>()
for object in arr{
if object[key] == keyVal{
tempArray.append(object)
}
}
for item in tempArray{
newArray.append(item)
}
tempArray.removeAll()
}
return newArray
}
Extension
You can created an extension available for Arrays of Dictionaries where both the Key and the Value are String(s).
extension Array where Element == [String:String] {
func sorted(by key: String) -> [[String:String]] {
return sorted { $0[key] ?? "" < $1[key] ?? "" }
}
}
Example
let crew = [
["Name":"Spook"],
["Name":"McCoy"],
["Name":"Kirk"]
]
crew.sorted(by: "Name")
// [["Name": "Kirk"], ["Name": "McCoy"], ["Name": "Spook"]]

How to check if my array of dictionaries has certain dictionary?

I have two arrays of dictionaries with type [String: String].
How can I check if one array contains dictionary from another.
let firstArray: [[String: String]] = [dict1, dict2, dict3]
let secondArray: [[String: String]] = [dict1, dict2, dict3, dict4, dict5]
I tried to do this with contains() method
for item in firstArray {
if secondArray.contains(item) {
print("Hello")
}
}
but it throw an error there. So what's the best way to do this?
You can use the predicate form of contains to accomplish this:
for item in firstArray {
if secondArray.contains(where: { $0 == item }) {
print("Hello")
}
}
You can't use the other form of contains because type [String : String] does not conform to the Equatable protocol.
This works fine in Swift 4
let dictionary = ["abc":"pqr"]
if !myArray.contains{ $0 == dictionary } {
//append dictionary inside array
myArray.append(dictionary)
}
else {
//dictionary already exist in your myArray
}

Swift 3.0 Remove duplicates in Array of Dictionaries

I am working removing the duplicate Dictionaries in an array of Dictionaries in swift 3.0
The below is the
let Dict1 : [String : String] = ["messageTo":"Madhu"]
let Dict2 : [String : String] = ["messageTo":"Kiran"]
let Dict3 : [String : String] = ["messageTo":"Raju"]
var arrOfDict = [[String:String]]()
arrOfDict.append(Dict1)
arrOfDict.append(Dict2)
arrOfDict.append(Dict1)
arrOfDict.append(Dict3)
arrOfDict.append(Dict2
print(arrOfDict)
//prints [["messageTo": "Madhu"], ["messageTo": "Kiran"], ["messageTo": "Madhu"], ["messageTo": "Raju"], ["messageTo": "Kiran"]]
As you can see there are 2 duplicate dictionaries in the arrOfDict.
can any one help me out in filtering the duplicates using Set or any other approach
Dictionaries does not conform to Hashable (or Equatable), so using Set is not an available approach in this case. For dictionaries where Key and Value types are Equatable, however, we have access to the == operator for readily performing a uniqueness filtering of the array of dictionaries:
public func ==<Key : Equatable, Value : Equatable>(
lhs: [Key : Value], rhs: [Key : Value]) -> Bool
E.g. as follows (O(n^2))
arrOfDict = arrOfDict.enumerated()
.filter { (idx, dict) in !arrOfDict[0..<idx].contains(where: {$0 == dict}) }
.map { $1 }
print(arrOfDict)
// [["messageTo": "Madhu"], ["messageTo": "Kiran"], ["messageTo": "Raju"]]
// or ...
arrOfDict = arrOfDict.enumerated()
.flatMap { (idx, dict) in !arrOfDict[0..<idx].contains(where: {$0 == dict}) ? dict : nil }
func removeDuplicates(_ arrayOfDicts: [[String: String]]) -> [[String: String]] {
var removeDuplicates = [[String: String]]()
var arrOfDict = [String]()
for dict in arrayOfDicts {
if let name = dict["messageTo"], ! arrOfDict.contains(name) {
removeDuplicates.append(dict)
arrOfDict.append(name)
}
}
return removeDuplicates
}
The reason why it is duplicated, because you add Dict1 and Dict2 2 times. May i ask why?
Set would not work for you, because Dictionary type does not conform to the Hashable protocol, only its key property.
You can do a validation on the dictionary, if there is already a value like that, do not append it to arrOfDict.
you can try this :
var set = NSSet(array: arrOfDict)

Resources