How do I sort an NSDictionary by integer value? - ios

I have a NSDictionary. When I print it, it looks like this:
{
3gZ0qtk0yMUEvtSmz2RW40Y7AC83 = 12;
USXhV0QYkpbSzAmXgfeteXHuoqg2 = 25;
UTPFBV5Q5IgTh17060c6WxNDQCO2 = 1;
fqsspZtskWVheqUQQtROepixsGB2 = 256;
}
I want to sort it so that it looks like this:
{
fqsspZtskWVheqUQQtROepixsGB2 = 256;
USXhV0QYkpbSzAmXgfeteXHuoqg2 = 25;
3gZ0qtk0yMUEvtSmz2RW40Y7AC83 = 12;
UTPFBV5Q5IgTh17060c6WxNDQCO2 = 1;
}
All of the methods I see needs a key name but I don't understand that. There is no key name. Sorry if this is duplicate but everything I try from other questions doesn't work. There's just too many ways out there and I get all kinds of errors and other problems so I want to know the best (latest), and most efficient way to sort NSDictionaries in this way.
Also it's coming from a firebase snapshot
example:
self.friendsDict = snapshot.value as! NSDictionary

try this -
let dict:NSMutableDictionary = [
"3gZ0qtk0yMUEvtSmz2RW40Y7AC83" : 12,
"USXhV0QYkpbSzAmXgfeteXHuoqg2" : 25,
"UTPFBV5Q5IgTh17060c6WxNDQCO2" : 1,
"fqsspZtskWVheqUQQtROepixsGB2" : 256
]
let sortedKeys2 = (dict as NSDictionary).keysSortedByValueUsingComparator
{
($1 as! NSNumber).compare($0 as! NSNumber)
}
OR
let dict = [
"3gZ0qtk0yMUEvtSmz2RW40Y7AC83" : 12,
"USXhV0QYkpbSzAmXgfeteXHuoqg2" : 25,
"UTPFBV5Q5IgTh17060c6WxNDQCO2" : 1,
"fqsspZtskWVheqUQQtROepixsGB2" : 256
]
var sortedKeys = Array(dict.keys).sort({dict[$0] > dict[$1]})

Since you said the solution from this link Swift sort dictionary by value didn't work, I am posting a working code on my side:
let dict:NSDictionary = [
"3gZ0qtk0yMUEvtSmz2RW40Y7AC83" : 12,
"USXhV0QYkpbSzAmXgfeteXHuoqg2" : 25,
"UTPFBV5Q5IgTh17060c6WxNDQCO2" : 1,
"fqsspZtskWVheqUQQtROepixsGB2" : 256
]
let d = dict as! [String:NSInteger]
for (k,v) in (Array(d).sort {$0.1 > $1.1}) {
print("\(k):\(v)")
}
Output:
fqsspZtskWVheqUQQtROepixsGB2:256
USXhV0QYkpbSzAmXgfeteXHuoqg2:25
3gZ0qtk0yMUEvtSmz2RW40Y7AC83:12
UTPFBV5Q5IgTh17060c6WxNDQCO2:1
Edit:
For above code to work, OP had to change:
self.friendsDict = snapshot.value as! NSDictionary
to:
self.friendsDict = snapshot.value as! [String:NSInteger]

Related

Traverse nsdictionary in swift

I am new to swift.
I have my dictionary as
monthData =
{
"2018-08-10" = {
accuracy = 71;
attempted = 7;
correct = 5;
reward = Bronze;
};
"2018-08-12" = {
accuracy = 13;
attempted = 15;
correct = 2;
reward = "";
};
"2018-08-13" = {
accuracy = 33;
attempted = 15;
correct = 5;
reward = "";
};
"2018-08-14" = {
accuracy = 100;
attempted = 15;
correct = 15;
reward = Gold;
};
"2018-08-16" = {
accuracy = 73;
attempted = 15;
correct = 11;
reward = Silver;
};
"2018-08-21" = {
accuracy = 26;
attempted = 15;
correct = 4;
reward = "";
};
"2018-08-23" = {
accuracy = 46;
attempted = 15;
correct = 7;
reward = "";
};
}
I want to get all the dates for which reward is Gold
Can anyone please help me do that?
What I have tried 'till now is:
for (key,value) in monthData{
let temp = monthData.value(forKey: key as! String) as! NSDictionary
for (key1,value1) in temp{
if((value1 as! String) == "Gold"){
print("keyFINAL \(key)")
}
}
but it outputs the error Could not cast value of type '__NSCFNumber' to 'NSString'
The error occurs because when you are iterating the dictionary you force cast the Int values to String which is not possible
The (highly) recommended Swift way is to use the filter function. This is much more efficient than a loop.
In the closure $0.1 represents the value of the current dictionary ($0.0 would be the key). The result is an array of the date strings.
let data : [String:Any] = ["monthData" : ["2018-08-10": ["accuracy" : 71, "attempted" ... ]]]
if let monthData = data["monthData"] as? [String:[String:Any]] {
let goldData = monthData.filter { $0.1["reward"] as? String == "Gold" }
let allDates = Array(goldData.keys)
print(allDates)
}
The code safely unwraps all optionals.
However if there is only one Gold entry the first function is still more efficient than filter
if let monthData = data["monthData"] as? [String:[String : Any]] {
if let goldData = monthData.first( where: {$0.1["reward"] as? String == "Gold" }) {
let goldDate = goldData.key
print(goldDate)
}
}
In Swift avoid the ObjC runtime (value(forKey:)) and Foundation collection types (NSDictionary) as much as possible.
From the first for in loop, you are getting the NSDictionary in temp variable
"2018-08-16" = {
accuracy = 73;
attempted = 15;
correct = 11;
reward = Silver;
};
So, you should directly check .value(forKey:) on temp and get the value for reward.
You should try it like this
for (key,value) in monthData {
let temp = monthData.value(forKey: key as! String) as! NSDictionary
if(((temp.value(forKey: "reward")) as! String) == "Gold"){
print("keyFINAL \(key)")
}
}
Try and share results
EDIT
Please checkout the answer from vadian for in-depth explanation and pure swift approach to achieve the same.
Thanks

am trying to print("_id") data which am getting from server

For the following swift code
self.cardDataArray = response.value(forKey: "card_list") as? NSArray
print(self.cardDataArray!)
i got this output from server(API)
(
{
"__v" = 0;
"_id" = 5978b5dadc336d0788a81c58;
"stu_number" = 1234567812345678;
"stu_status" = 1;
"created_at" = "2017-07-26T15:31:38.874Z";
"stu_id" = 5978b41ddc336d0788a81c57;
"stu_number" = 1234;
"default_status" = 0;
"default_type" = 3;
}
)
am trying to print "_id" from above code
but am getting error
Could not cast value of type '__NSSingleObjectArrayI' (0x1a9ae6ca0) to 'NSString'
here is the code which i tried to print
let studentID = self.cardDataArray?.value(forKey: "_id") as! NSString
print(studentID)
Your cardDataArray is a Dictionaries Array so you must first take this dictionary and access to the key you need in this case "_id", try with this code
if let studentDict = self.cardDataArray?[0] as? NSDictionary
{
print(studentDict.object(forKey: "_id") as? NSString)
}
Updated
for dictObj in self.cardDataArray {
print(dictObj.object(forKey: "_id") as? NSString)
}
Hope this helps

Applying filter and calculating Average on NSDictionary with JSON origin

I read some posts here and google about it, but still couldn't understand how to do a simple filter using the swift filter feature. I am new to Swift and functional programing, so forgive me if that is too basic.
I have the following JSON:
{
"-KjirKH7Bo7c5vq7ZH9N" = {
rank = 2;
placa = "xxx-0003";
uid = yNpL0uzI5LRj6etFGVgoWYEK2E52;
};
"-Kjiyi_i7FLl6dks6xKL" = {
rank = 5;
placa = "xxx-0003";
uid = yNpL0uzI5LRj6etFGVgoWYEK2E52;
};
}
I was able to create an array of the values with:
if let dict = snapshot.value as? NSDictionary{
let myArray = dict.map{$0.value} //array of values
}
Which creates this:
[{
rank = 5;
placa = "xxx-0003";
uid = yNpL0uzI5LRj6etFGVgoWYEK2E52;
}, {
rank = 2;
placa = "xxx-0003";
uid = yNpL0uzI5LRj6etFGVgoWYEK2E52;
}]
My goal now is:
Apply a filter to retrieve only items that the property "rank" is greater than 0.
After that I want to calculate the items rank average (in this example 2+5/2 = 3.5).
I have tried this:
myArray.filter{$0.rank > 0 }
But it fails with "Value of type 'Any' has no member 'rank'"
Any idea how I can filter this array?
I have tried with NSPredicate, but I am wondering if there is some way to take advantage of the native filter.
Looks like you have two issues:
myArray's items are of type Any and in reality they are dictionaries so you have to help compiler to understand that. Here is how to do it:
let myArray: [[String: Any]] = dict.map{ $0.value }
Because myArray contains dictionaries, you have to modify accessing values in filter:
myArray.filter{ (($0["rank"] as? Int) ?? 0) > 0 }
Hope it helps!
myArray.filter{ ((($0 as! NSDictionary)["rank"] as? Int) ?? 0) > 0 }
appending the answer of K.K Cast $0 to NSDictionary. I hope that will work

How to Sort an array in an ascending order swift 2.3

[{
msg = "Hi This is Jecky";
name = Susheel;
sender = 77;
timestamp = 1464241769520;
username = susheel;
}, {
msg = Dubai;
name = Jecky;
sender = 78;
timestamp = 1464246547147;
username = Jecky;
}, {
msg = "How are you ?";
name = Susheel;
sender = 77;
timestamp = 1464243480381;
username = susheel;
}, {
msg = "Aje dekhai nai";
name = Jecky;
sender = 78;
timestamp = 1464244974198;
username = Jecky;
}]
I have an array like this. I want to sort this array using timestamp in swift 2.3 or latest version of swift. Can anyone help me for this ?
let array=[
[
"msg":"Hi This is Jecky",
"name":"Susheel",
"sender":77,
"timestamp":1464241769520,
"username":"susheel",
],
[
"msg":"Dubai",
"name":"Jecky",
"sender":78,
"timestamp":1464246547147,
"username":"Jecky",
],
[
"msg":"How are you ?",
"name":"Susheel",
"sender":77,
"timestamp":1464243480381,
"username":"susheel",
],
[
"msg":"Aje dekhai nai",
"name":"Jecky",
"sender":78,
"timestamp":1464244974198,
"username":"Jecky",
],
]
print("array = \(array)")
let sortedArray=array.sort { (obj1, obj2) -> Bool in
return (obj1["timestamp"] as! Double) < (obj2["timestamp"] as! Double)
}
print("sortedArray = \(sortedArray)")
If your array is mutable you can user sortInPlace
yourArray.sortInPlace{$0.timestamp < $1.timestamp}
and if not, you can create a new array from sort, like suggested by Kristijan (although no need for parentheses on trailing closures):
let newArray = yourArray.sort{$0.timestamp < $1.timestamp}
You can get this functionality using extension:
extension NSArray{
//sorting- ascending
func ascendingArrayWithKeyValue(key:String) -> NSArray{
let ns = NSSortDescriptor.init(key: key, ascending: true)
let aa = NSArray(object: ns)
let arrResult = self.sortedArray(using: aa as! [NSSortDescriptor])
return arrResult as NSArray
}
//sorting - descending
func discendingArrayWithKeyValue(key:String) -> NSArray{
let ns = NSSortDescriptor.init(key: key, ascending: false)
let aa = NSArray(object: ns)
let arrResult = self.sortedArray(using: aa as! [NSSortDescriptor])
return arrResult as NSArray
}
}
use like this:
let array=[
[
"msg":"Hi This is Jecky",
"name":"Susheel",
"sender":77,
"timestamp":1464241769520,
"username":"susheel",
],
[
"msg":"Dubai",
"name":"Jecky",
"sender":78,
"timestamp":1464246547147,
"username":"Jecky",
],
[
"msg":"How are you ?",
"name":"Susheel",
"sender":77,
"timestamp":1464243480381,
"username":"susheel",
],
[
"msg":"Aje dekhai nai",
"name":"Jecky",
"sender":78,
"timestamp":1464244974198,
"username":"Jecky",
],
]
let a = NSArray.init(array: array)
let filArray = a.ascendingArrayWithKeyValue(key: "timestamp")
print(filArray)
customArray.sortInPlace {
(element1, element2) -> Bool in
return element1.someSortableField < element2.someSortableField
}
Check this out
https://www.hackingwithswift.com/example-code/arrays/how-to-sort-an-array-using-sort
To sort by property "timestamp"
array.sorted{$1["timestamp"] as? Long > $0["timestamp"] as? Long}
=> First, convert your Json to Objects. (check this link to do that :- http://roadfiresoftware.com/2015/10/how-to-parse-json-with-swift-2/ )
=> Then declare your Array as a typed array so that you can call methods when you iterate:
var array : [yourObjectClassName] = []
=> Then you can simply sort the value by :
array.sort({ $0.name > $1.name })
The above example sorts all the arrays by name. If you need to sort by timeStamp you can change the name to timeStamp ..etc
Check this link for more sorting examples : Swift how to sort array of custom objects by property value

Swift Accessing Values in Dictionary

I have a swift dictionary and I am trying to access my values in my array.
My Dictionary that I make looks like this:
["results": {
Democrats = {
percent = 67;
raw = 4;
};
Republicans = {
percent = 33;
raw = 2;
};
"total_answers" = 6;
}, "success": 1]
I made another dictionary to get this:
let test = dictionary["results"] as! [String : AnyObject]
["Democrats": {
percent = 67;
raw = 4;
}, "Republicans": {
percent = 33;
raw = 2;
}, "total_answers": 6]
I can access values like:
let testing = test["total_answers"]
I want to access the values for percent and raw for example:
Democrats = {
percent = 67;
raw = 4;
};
The percent and raw key are static but the Democrats is a string that will never be the same.
I don't know what notation you're using for your dictionary but it doesn't compile in Swift.
A dictionary with [String:Any] as type would work but manipulating the data is going to be a type casting nightmare. You should consider using a regular structure where all values have the same type.
For example (using a typealias for the two value tuple):
typealias Votes = (percent:Int, raw:Int)
var results = [ "Democrats" : Votes(percent:67, raw:4),
"Repubicans" : Votes(percent:33, raw:2),
"Totals" : Votes(percent:100, raw:6)
]
let democratVotes = results["Democrats"]!.raw
So here let democrats:[String : Int] = test["Democrats"] as! [String : Int] will provide you new dictionary, containing only
Democrats = {
percent = 67;
raw = 4;
};

Resources