Swift Sort NSDictionary of NSDictionary by Value - swift

I have a NSDictionary of NSDictionaries and I was wondering how do I sort the dictionaries by their value (Name)? The number will change from 0-n. Below is how the structure is setup:
let attribute = cumstomClass.Attribute!
attribute =
-Krqq2AWOqWKys0siTmc
Description: ""
Name: "B"
Value: ""
-KrqpSvSX7eKqYfvu5ZO
Description: ""
Name: "A"
Value: ""
Thanks in advance! :)

Since a Dictionary is inherently unsorted, you'll need to use an Array instead. Fortunately, this is easily done via functional techniques:
guard let dict = nsDict as? [String : [String : String]] else { /* handle the error */ }
let sorted = dict.sorted { ($0.value["Name"] ?? "") < ($1.value["Name"] ?? "") }
print(sorted)
This will get you an Array of tuples containing the keys and values of the respective dictionaries, sorted by "Name":
[(key: "KrqpSvSX7eKqYfvu5ZO", value: ["Name": "A", "Value": "", "Description": ""]),
(key: "Krqq2AWOqWKys0siTmc", value: ["Name": "B", "Value": "", "Description": ""])]
Alternately, you can simply get a sorted array of the String keys:
let sortedKeys = dict.sorted { ($0.value["Name"] ?? "") < ($1.value["Name"] ?? "") }.map {
$0.key
}
print(sortedKeys)
which outputs:
["KrqpSvSX7eKqYfvu5ZO", "Krqq2AWOqWKys0siTmc"]
This array of keys can later be used to dynamically look up objects from the dictionary at runtime when populating the collection view. This method will be likely to be more expensive performance-wise than the first approach due to all the dictionary lookups, but will result in lower memory usage if you also need to keep the original dictionary around in its original form for some reason.

Related

How to Sort the first dimension of a 2D Array Swift

Im trying to sort the columns of a CSV file,the contents of the CSV is provided in string
Beth,Charles,Danielle,Adam,Eric\n
17945,10091,10088,3907,10132\n
2,12,13,48,11
Converted String to 2D Array
[["Beth", "Charles", "Danielle", "Adam", "Eric"], ["17945", "10091", "10088", "3907",
"10132"], ["2", "12", "13", "48", "11"]]
How can i sort the only the first dimension of the 2D array or the Names in the 2D Array and still keep the mappings of the other dimension, i don't know how to explain this properly, but i hope the details below will help you understand what i want to achieve.
Adam,Beth,Charles,Danielle,Eric\n
3907,17945,10091,10088,10132\n
48,2,12,13,11
I want to achieve this with the names sorted and the other values in the other arrays mapping to the names like below,
[["Adam", "Beth", "Charles", "Danielle", "Eric"], ["3907", "17945", "10091", "10088",
"10132"], ["48", "2", "12", "13", "11"]]
Using this approach is not working but sorts the whole array
let sortedArray = 2dArray.sorted(by: {($0[0] as! String) < ($1[0] as! String) })
[["3907", "17945", "10091", "10088", "10132"], ["48", "2", "12", "13", "11"], ["Adam", "Beth", "Charles", "Danielle", "Eric"]]
Below if the full code
var stringCSV =
"Beth,Charles,Danielle,Adam,Eric\n17945
,10091,10088,3907,10132\n2,12,13,48,11";
var csvFormatted = [[String]]()
stringCSV.enumerateLines { line , _ in
var res = line.split(separator: ",",omittingEmptySubsequences:
false).map{ String($0) }
for i in 0 ..< res.count {
res[i] = res[i]
}
csvFormatted.append(res)
}
print(csvFormatted)
let sortedArray = csvFormatted.sorted(by: {($0[0] as! String)
< ($1[0] as! String) })
print(sortedArray)
Using "associated" arrays always ends up being messy.
I would start by creating a struct to represent each object (You haven't said what the numbers are, so I have picked a couple of property names. I have also kept String as their type, but converting to Int is possibly better, depending on what the data actually represents).
struct Person {
let name: String
let id: String
let age: String
}
Now you can combine the arrays and use that to build an array of these structs. Then you can sort by the name property.
let properties = zip(sourceArray[1],sourceArray[2])
let namesAndProperties = zip(sourceArray[0],properties)
let structArray = namesAndProperties.map { (name,properties) in
return Person(name: name, id: properties.0, age: properties.1)
}
let sortedArray = structArray.sorted {
return $0.name < $1.name
}

Removing Non Duplicate Keys from Two Dictionary

I have two dictionaries in Swift with few similar values which are in dynamic mode:
dict1 = ["a1":"value 1", "b1":"value2", "c1":"value 3"]
dict2 = ["b1": "value2", "d1": "value4"]
If I want to compare these two dictionaries and want to extract only the matching keys even nested, how do I about to do that?
If you want the common keys with the value in one of them :
let intersectionDict = dict1.filter { dict2.keys.contains($0.key) }
//Or
let intersectionDict2 = dict2.filter { dict1.keys.contains($0.key) }
If you want the values to match too:
let intersectionDict3 = dict1.filter { dict2[$0.key] == $0.value }
And the result is:
print(intersectionDict3) //["b1": "value2"]
As others have shown, you can do this using a filter statement. You can make it even quicker by always filtering the smaller of the two dicts, improving the time complexity from O(dict1.size) to O(min(dict1.size, dict2.size).
extension Dictionary {
func intersectingByKeys(with other: Dictionary) -> Dictionary {
let (smallerDict, largerDict) = (self.count < other.count) ? (self, other) : (other, self)
return smallerDict.filter { key, _ in largerDict.keys.contains(key) }
}
}
let dict1 = ["a1":"value 1", "b1":"value2", "c1":"value 3"]
let dict2 = ["b1": "value2", "d1": "value4"]
print(dict1.intersectingByKeys(with: dict2))
You can create a Set from the keys of one of the dictionaries and call intersection on the Set with the keys of the other dictionary.
let matchingKeys = Set(dict1.keys).intersection(dict2.keys) // {"b1"}

Swift, Dictionary of String:String, best way to get an array of values, where the key contains a string?

I have a dictionary, like this:
{"Matt":"Apple", "Calvin":"Nut", "Susie":"Pear"}
I want to check the keys of the dictionary with a string, to see if they contain that string-- and if they do, I want to add their respective value to an array
So, if the input was "a"
the return would be ["Apple", "Nut"] because "Matt" and "Calvin" matched the "a"
Basically looking for some Swift tips,
otherwise I was going to implement it like this:
Grab all keys, put into an array
Filter keys for string value, this is the keyArray
Enumerate over keyArray, and get all their values from the dictionary
Boom have an array of values
You can filter the dictionary, using contains() on each key,
and then extract the corresponding values:
let dict = [ "Matt":"Apple", "Calvin":"Nut", "Susie":"Pear" ]
let input = "a"
let values = Array(dict.filter { $0.key.contains(input) }.values)
print(values) // ["Apple", "Nut"]
Or with flatMap() (renamed to compactMap() in Swift 4.1):
let values = dict.flatMap { $0.key.contains(input) ? $0.value : nil }
print(values) // ["Apple", "Nut"]
Here each dictionary entry is mapped to the value if the key contains
the given string, and to nil otherwise. flatMap() then returns an
array with the non-nil values.
Simply using a filter over the dictionary:
let dict: [String: String] = ["Matt": "Apple", "Calvin": "Nut", "Susie": "Pear"]
func findValuesMatching(search: String, in dict: [String: String]) -> [String] {
return dict
.filter { (key, value) in
return key.range(of: search) != nil
}.map { (key, value) in
return value
}
}
print(findValuesMatching(search: "a", in: dict))
Do it like this:
let dict = ["Matt":"Apple", "Calvin":"Nut", "Susie":"Pear"]
let containsA = dict.filter({ $0.key.lowercased().contains("a") }).map({ $0.value })
Output:
["Apple", "Nut"]

Generate section headers from dictionary [Swift 3]

I have an array of dictionaries with the following type of structure (which is already sorted) :
[
[
"id": 1,
"name": "ItemA",
"url": "http://url.com"
],
[
"id": 32,
"name": "ItemB",
"url": "http://url.com"
],
...
]
Declared as an array of dictionaries for AnyObject :
var arrayApps = [[String:AnyObject]]()
This array of dictionaries is generated using SwiftyJson :
[..]
if let resData = swiftyJsonVar["data"].arrayObject {
self.arrayItems = resData as! [[String:AnyObject]]
}
[..]
My Goal is to display those items in sections by using the sections headers but after trying to figure it out and looking for an answer, i'm unable to move on.
I've tried to groupe the dictionaries by letters to get a result like this:
[
"A":{[foo1],[foo2]},
"D":{[foo3],[foo5]},
"F":{[foo4],[foo6]}
...
]
But no luck, i've always ended up with errors because my array contains "Optionals".
In summary :
How can I generate Alphabetical section headers based on the name inside a TableView using an array of dictionaries not grouped like the one given above in Swift 3 ?
Thank you in advance !!
You can use the .sorted(by: ) method of Array to compare to elements of you array with each other.
This yields a sortedArray:
let sortedArray = arrayOfApps.sorted(by: {($0["name"] as! String) <= ($1["name"] as! String)})
This will crash if the itemName is not a String but I left it to you to handle any errors. For example changing it to:
$0["name"] as? String ?? ""
EDIT:
// Removed examples and added extension to create desired result
I found one of my old projects where I wrote such extension. Changed it a bit to suit your needs, tell me if it needs some change still:
extension Array {
func sectionTitlesForArray(withName name: (Element) -> String) -> Array<(title: String, elements: NSMutableArray)> {
var sectionTitles = Array<(title: String, elements: NSMutableArray)>()
self.forEach({ element in
var appended = false
sectionTitles.forEach({ title, elements in
if title == name(element) {
elements.add(element)
appended = true
}
})
if appended == false {
sectionTitles.append((title: name(element), elements: [element]))
}
})
return sectionTitles
}
}
// Usage single letter as Section title:
let sectionTitles = arrayOfApps.sectionTitlesForArray(withName: {
let name = $0["name"] as! String
return String(name[name.startIndex])
})
// Quick dirty pretty-print:
sectionTitles.forEach({ sectionTitle in
print("Section title: \(sectionTitle.title) \n")
sectionTitle.elements.forEach({ object in
let element = object as! Dictionary<String,Any>
print("Element name: \(element["name"]!)")
})
print("")
})

Pick value stored against a hash key which ends with a specific character

I have a hash of type [String: [String: AnyObject]]() something like below,
{
"asndfkjsndkjnf_1": {"A": "2"},
"jckxjbcvkjbsdsdfkz_2": {"G": 3},
"sahgkshbgskdssjf_3": {"T": '1'},
"asdhlsfldsnfsldk_4": {"C": 4}
}
I want to pick the value of hash entry whose key ends with "_1", since hashes don't have indexes I am finding it a bit difficult to retrieve item I want without knowing the complete key.
Dictionary has a keys property that lets you (lazily) evaluate all the keys of the dictionary, so you can just use:
let foo = [
"asndfkjsndkjnf_1": ["A": "2"],
"jckxjbcvkjbsdsdfkz_2": ["G": 3],
"sahgkshbgskdssjf_3": ["T": "1"],
"asdhlsfldsnfsldk_4": ["C": 4]
]
if let match = foo.keys.filter({ (key) -> Bool in key.hasSuffix("_1") }).first {
// foo[match] will be the first matched dictionary
print("\(foo[match])")
}
another possibility is to just filter the whole dictionary for matching keys:
foo.filter({ $0.0.hasSuffix("_1") }).first?.1
The normal way of fetching a value from a dictionary by using optional binding:
let dict: [String:AnyObject] = [
"asndfkjsndkjnf_1": ["A": "2"],
"jckxjbcvkjbsdsdfkz_2": ["G": 3],
"sahgkshbgskdssjf_3": ["T": "1"],
"asdhlsfldsnfsldk_4": ["C": 4]
]
if let innerDict = dict["asndfkjsndkjnf_1"] as? [String:AnyObject] {
print(innerDict)
if let valueA = innerDict["A"] as? String {
print(valueA)
}
}
But in a comment you say
I don't know the complete key to retrieve the value, I only know that the hash's key ends with a certain pattern
You can use .hasSuffix() to find which key ends with a certain string, then use this key to fetch the inner value.
Let's say you want to find the value for the key ending with "_3":
for key in dict.keys where key.hasSuffix("_3") {
if let innerDict = dict[key] as? [String:AnyObject] {
print(innerDict)
if let innerValue = innerDict["T"] as? String {
print(innerValue)
}
}
}