Swift: Sort Dictionary of Number Strings - ios

I have a Dictionary of number String that looks like this:
let comph : [String : [String : String]] = [
"1":["title" : "first title"],
"2":["title" : "second title"],
"10": ["title" : "last title"]
]
Then when I sort my Dictionary using comph.sort { $0.0 < $1.0 } I get this:
let sortedComph = comph.sort { $0.0 < $1.0 }
print(sortedComph) //["1":["title" : "first title"],"10": ["title" : "last title"],"2":["title" : "second title"]]
How can I sort the Dictionary so that it returns the keys in numerical order?
eg: ["1":["title" : "first title"],"2":["title" : "second title"],"10": ["title" : "last title"]]
Thanks'

Dictionaries in Swift, by definition, are unsorted collections. You'll need to use some other type if you want to maintain order.
But, it looks like this guy wrote an OrderedDictionary in Swift, if you're interested:
Learning Swift: Ordered Dictionaries
Or, a quick n' dirty solution would be to extract the keys to an array, sort those, then iterate over the array and do a dictionary lookup using the keys.

The sortedComph constant in your example is no longer a dictionary, but an array of (key, value) tuples. The problem you're running into is that when sorting strings, the comparison is done without treating the strings as numeric values. That's what you need to fix.
One way to handle this is by creating Int? instances from your strings in your comparison function:
let sortedComph = comph.sort { Int($0.0) < Int($1.0) }
// [("1", ["title": "first title"]), ("2", ["title": "second title"]), ("10", ["title": "last title"])]
That way the resulting array will be sorted by the integer value of what's in the string keys. Any non-Integer keys will be grouped at the beginning of the list, since that's how nil sorts.
The more robust way is probably to use the NSString.compare(:options:) method:
let sortedComph = comph.sort {
($0.0 as NSString).compare($1.0, options: .NumericSearch) == .OrderedAscending
}
// [("1", ["title": "first title"]), ("2", ["title": "second title"]), ("10", ["title": "last title"])]

if you want you could have an array of structure representing your data
struct Model
{
var key:String!
var title:String!
// etc...
}
Then declare the array of that structure:
var arrayOfStructure = [Model]()
Then you sorted it

Related

Sort dictionary of arrays by both key and values in Swift

I have a dictionary like this:
dict = {
"a": ["apple", "aeroplane", "ash"],
"c": ["cat", "car"],
"b": ["boy", "bit", "bee"]
}
I would like to sort it using swift and the result should be like this:
dict = {
"a": ["aeroplane", "apple", "ash"],
"b": ["bee", "bit", "boy"],
"c": ["car", "cat"]
}
Both the keys and the values in the array should be in alphabetical order. However, I can only successfully sort the keys using .keys.sorted() and I failed to sort the array in each of the dictionary values in alphabetical order.
First of all I just want to remark that it's a bit redundant to sort the keys of a dictionary because you check values associated with a given key, not where the value is, it makes no sense to order them.
Second about sorting the arrays inside the dictionary it's pretty easy, first you need to iterate over the dictionary using for each, then since the arrays are immutable after you call .sorted() you assign the result to the key associated with the value you just received.
var dict : [String:[String]] = [
"a" : ["apple", "aeroplane", "ash"],
"c" : ["cat", "car"],
"b" : ["boy", "bit", "bee"]
]
// sort arrays inside an item of the dictionary
for (key,value) in dict {
dict[key] = value.sorted()
}
print(dict)

same value in dictionary in swift

I m mapping data that come from service with using dictionary [String: String]. I collect them dictionary array. For example, if their parent ids are the same, I want to add their values by array value.
["ParentId": "1","Value": "["Giyim","Aksesuar","Ayakkabı"]"]
It is also the reason I don't know parent id sometimes on the left sometimes on the right in photo
Here is my code and its output.
struct Categories {
let parentId: String
let values: [String]
}
for result in results {
if result?.parentCategoryId != "" {
for docId in self.docIds {
if result?.parentCategoryId == docId {
//print(result?.name)
var values = [String]()
values.append(result?.name ?? "")
self.newCat.append(Categories(parentId: docId, values: values))
}
}
}
}
Problem
As far as I understand from the description you want to map some service data structure to a dictionary where key is parentId and value is an array of some items referred to parentId.
I believe your problem comes from a misunderstanding of the concept of dictionary as a data structure.
[String: String] is dictionary where keys and their associated values are of String type. For example:
["firstKey": "firsthValue", "secondKey": "secondValue", ...]
That means you cannot store associated values of String and Array types in the same dictionary, as you already told the compiler you would like to store only strings.
It is also the reason I don't know parent id sometimes on the left sometimes on the right in photo
This is because key-value pairs are stored in the dictionary without order. This is how dictionaries work :) I'd strongly recommend reading some short official materials to get used to them.
New Swift 5.4 version has a new OrderedDictionary data structure, where keys are ordered, but there is absolutely 100500% no reason to use it for your problem*
Possible solutions
In your case i would suggest either use some struct:
struct SomeData {
let parentID: Int
var values: [String]
}
var storage: [SomeData] // alternative to emptyDic
// Filling storage with data from service
for result in inputData {
// search for SomeData with required id, add value
// OR create SomeData if there is no such id in array yet
}
OR [personally this appeals to me more]
Store data in [String: [String]] dictionary, where the key is parentID and the associated value is an array of values.
The algorithm of filling this dictionary is pretty the same:
You add new key-value pair for every new parentID
You append new values for parentIDs that are already in the dictionary.
Using the struct approach, you could do something like this (you'll need to adapt it to your code, but that should be straightforward):
struct Categories {
let parentId: String
var values: [String] //notice this needs to be a var, not a let
}
func addItem(categories : inout [Categories], docId: String, name: String) {
if let index = categories.firstIndex(where: { $0.parentId == docId }) {
categories[index].values.append(name)
} else {
categories.append(Categories(parentId: docId, values: [name]))
}
}
func addValues() {
var categories = [Categories]()
addItem(categories: &categories, docId: "4", name: "Test1")
addItem(categories: &categories, docId: "1", name: "Test")
addItem(categories: &categories, docId: "4", name: "Test2")
addItem(categories: &categories, docId: "4", name: "Test3")
print(categories)
//in your code, it'll look more like:
// addItem(categories: &self.newCat, docId: docId, name: result?.name ?? "")
}
Which yields this:
[
StackOverflowPlayground.Categories(parentId: "4", values: ["Test1", "Test2", "Test3"]),
StackOverflowPlayground.Categories(parentId: "1", values: ["Test"])
]
I still wonder whether you maybe just want a Dictionary that is keyed by the parentId, but not knowing your use case, it's hard to say.

Dictionary [String: String] keys order changed when converted to array swift

I have a dictionary like below,
var dataSource: [String: String] = ["FirstName": "Austin",
"ListName": "Michael",
"Address": "Street Address",
"City": "Chennai"]
I want to populate these values in a UITableView, so I tried to get all the keys from Dictionary to an Array like below,
let dataArray = Array(dataSource.keys)
I got the output as [String] like,
["LastName", "FirstName", "City", "Address"]
The problem is, the order of the keys has changed, I want the array in the same order as dictionary has.
Can you anyone help?
Use plain dictionary as tableview datasource is bad idea.
However Dictionary can not be sorted. So it doesn't matter in what order you add your keys-values to the dictionary.
If you need sorted then use array of dictionary instead.
You should use models instead of plain dictionary that is easy to maintain :]
like
struct User {
var firstName:String?
var lastName:String?
var address:String?
var city:String?
}

Iterating through an array of dictionaries to get all the keys

I have an array of dictionaries [[String : AnyObject]] called rooms in a User object - each dictionary holds a name as the String and an id as the AnyObject.
I want to populate a table view with the names, so I'm trying to loop through the array and grab the String values from the dicts.
if let roomDict = myUser.rooms as? [[String : AnyObject]] {
for (roomNames, _) in roomDict {
cell.textLabel?.text = roomNames
}
}
I'm relatively new and from what I've seen in tutorials and such when looping through dictionaries, you use the underscore to specify that you don't want the second value. So just grab all the first values (in this case, the names), and set them to the roomNames variable.
However I'm confused here because I'm not looping through a dictionary, I'm looping through an array of dictionaries. So I'm not sure how to do that. I did a search and the results I saw generally were asking about JSON, which isn't the case here. How can I do this?
Thanks for any help!
Firstly, your dictionary structure isn't ideal. Rather than having the key as the room name and the value as the identifier, your dictionary should have known key names, with variable values. Keys should not be "data" in a dictionary.
So, rather than
["room1":1]
it would be better if it were
["roomName":"room1", "roomID":1]
with your current structure, however, assuming that there is only one key per dictionary and that is the room name, you can get the names with:
if let rooms = myUser.rooms as? [[String : AnyObject]] {
roomNames = rooms.map({ $0.keys.first!})
}
If you use the better structure I suggested then it would be
if let rooms = myUser.rooms as? [[String : AnyObject]] {
roomNames = rooms.map({ $0["roomName"] as? String ?? ""})
}
I'm not clear about your data structure. If you have an array of dictionaries, and you want all the keys, you could use code like this:
let array = [
["key1": "value1",
"key2": "value2",
"key3": "value3",
"key4": "value4"],
["key5": "value5",
"key6": "value6",
"key7": "value7",
"key8": "value8"],
["key9": "value9",
"key10": "value10",
"key11": "value11",
"key12": "value12"]
]
let keys = array
.map{$0.keys}
.reduce([], +)
print(keys)
That will give you an array of all the keys from all the dictionaries. The keys from each dictionary will be in a jumbled order however, since dictionaries are unordered. You'll get the keys from each inner dictionary in a jumbled order, followed by the keys from the next dictionary in the array, etc.
Sample output:
["key2", "key3", "key4", "key1", "key7", "key8", "key5", "key6", "key9", "key10", "key12", "key11"]
If you want to sort the keys, you can do that:
let keys = array
.map{$0.keys}
.reduce([], +)
.sorted{$0.compare($1, options: .numeric) == .orderedAscending}
(In the above code I'm using the String class's compare(_:options:) function with an options value of .numeric so that "key11" sorts after "key10" instead of ["key1", "key11", "key12", "key2"], which you get from standard string ordering.)
The output of the sorted version (with the .numeric option) is:
["key1", "key2", "key3", "key4", "key5", "key6", "key7", "key8", "key9", "key10", "key11", "key12"]
Without the .numeric option, the output would be:
["key1", "key10", "key11", "key12", "key2", "key3", "key4", "key5", "key6", "key7", "key8", "key9"]
If your keys contain mixed upper/lower case and you want to ignore it, you'd use options of [.numeric, .caseInsensitive] (case insensitive sorting where numbers within strings are compared using numeric value.)

Update multiple labels with values from a dictionary?

I'm trying to make an app that will select an index at random, something like 0-1000 and then print the key, value, and link of the selected number to three seperate labels on the iPhone simulator.
So from my example, I want to randomly select "0" or "1" and if for instance "1" was chosen; then the key, value, and link information would each be printed to three separate labels on the simulator. The following is what I've been working on in playgrounds. Is there a better way to go about this?
var spDictionary: [String: [String:String]] = [
"0": ["key": "AMZN", "value": "AMAZON", "link": "yahoo"],
"1": ["key": "AAPL", "value": "APPLE", "link": "yahoo2"],
]
And for the random aspect I think it would be something like this but I'm not sure? Sorry for the newbie question.
let randomIndex: Int = Int(arc4random_uniform(UInt32(spDictionary.count)))`
Even for small data structures it's worth it to create a custom class or struct
struct Data {
let key : String
let value : String
let link : String
}
Create an object
let data = Data(key: "AMZN", value: "AMAZON", link: "yahoo")
Get a property
let link = data.link
and you can declare your dictionary
var spDictionary : [String: Data] = ...

Resources