How do you sort NSMutableArray from JSON in swift? - ios

I made a UITableview with NSMutableArray, having data downloaded from server in form of JSON. When I perform the table cell the code goes like below.
if let rstrntName = self.items[indexPath.row]["rstrnt_name"] as? NSString {
cell.rstrntName.text = rstrntName
}
Now I want to sort it by a column named "rstrnt_name". Below is the code I tried, but it doesn't work.
self.items.sortedArrayUsingComparator({obj1, obj2 -> NSComparisonResult in
let rstrnt1: NSDictionary = obj1 as NSDictionary
let rstrnt2: NSDictionary = obj2 as NSDictionary
if (rstrnt1["rstrnt_name"] as String) < (rstrnt2["rstrnt_name"] as String) {
return NSComparisonResult.OrderedAscending
}
if (rstrnt1["rstrnt_name"] as String) > (rstrnt2["rstrnt_name"] as String) {
return NSComparisonResult.OrderedDescending
}
return NSComparisonResult.OrderedSame
})
How can I sort objects in such type?

Assign the sorted array to anywhere and check -
self.items = self.items.sortedArrayUsingComparator(//rest of your code
should give you the sorted result.

self.items.sortedArrayUsingComparator returns a sorted array which you appear to be throwing away to the ether. Try storing the value instead.

Here is swift Array types sort method with pattern matching. This sort method directly mutates items doesn't return new one.
var items: [[String: AnyObject]] = [["rstrnt_name": "mustafa"], ["rstrnt_name": "Ray"], ["rstrnt_name": "Ali"]]
items.sort { (left, right) -> Bool in
let first = left["rstrnt_name"] as? String
let second = right["rstrnt_name"] as? String
switch (first, second) {
case let (.Some(x), .Some(y)): return x < y
default: return false
}
}

Related

Check element in array with different objects

I have an array with custom objects by 2 types. Also I have TableView, which shows objects from array. I need to select tableViewCell and check, if the element already in array - remove it from array, otherwise add it to array. I know, there is method for the checking array.contains(element) but my array looks like [Any] and it doesn't have this method.
I'm trying to check it with use for-in, but it's not good solution.
How can I do this?
let a: Int = 5
let b: String = "3"
let array: [Any] = [a, b]
You are able to cast Any to Int or String type and just use array.contains
array.contains {
if let intValue = $0 as? Int {
return intValue == 3
} else if let stringValue = $0 as? String {
return stringValue == "3"
}
return false
}
OR use this extension (Swift 4):
extension Array where Element: Any {
func contains<T: Equatable>(_ element: T) -> Bool {
return contains {
guard let value = $0 as? T else { return false }
return value == element
}
}
}
array.contains("3") // true for your example

swift 3 filter array of dictionaries by string value of key in dictionary

I have a class such as this
class FoundItem : NSObject {
var id : String!
var itemName : String!
var itemId : Int!
var foundBy : String!
var timeFound : String!
init(id: String,
itemName: String,
itemId: Int,
foundBy: String,
timeFound: String)
{
self.id = id
self.itemName = itemName
self.itemId = itemId
self.foundBy = foundBy
self.timeFound = timeFound
}
and I reference it on my
class MapViewVC: UIViewController, MKMapViewDelegate {
var found = [FoundItem]()
var filterItemName : String()
}
My FoundItem are generated by into an array of dictionaries from my class of FoundItem from a firebase query. I then get a string of that itemName that is generated from an another view controller that is a collection view on the didSelection function. I want to take that string and then filter or search the arrays with the string itemName that is equal from the itemName string from my previous viewController. Then removed the array of dictionaries that are not equal to the itemName. Not just the objects, but the entire array that contains non-equal key, value pair. I have looked for days, and I am stuck on filtering an array of dictionaries created from a class. I have looked and tried NSPredicates, for-in loops, but all that ends up happening is creating a new array or bool that finds my values or keys are equal. Here is the current function I have written.
func filterArrayBySearch() {
if self.filterItemName != nil {
dump(found)
let namePredicate = NSPredicate(format: "itemName like %#", "\(filterItemName)")
let nameFilter = found.filter { namePredicate.evaluate(with: $0) }
var crossRefNames = [String: [FoundItem]]()
for nameItemArr in found {
let listName = nameItem.itemName
let key = listName
if crossRefNames.index(forKey: key!) != nil {
crossRefNames[key!]?.append(nameItemArr)
if !("\(key)" == "\(filterItemName!)") {
print("------------- Success have found [[[[[[ \(key!) ]]]]]] and \(filterItemName!) to be equal!!")
// crossRefNames[key!]?.append(nameItemArr)
} else {
print("!! Could not find if \(key!) and \(filterItemName!) are equal !!")
}
} else {
crossRefNames[key!] = [nameItemArr]
}
}
} else {
print("No Data from Search/FilterVC Controller")
}
}
Can anyone help? It seems like it would be the simple task to find the value and then filter out the dictionaries that are not equal to the itemName string, but I keep hitting a wall. And running into for-in loops myself :P trying different things to achieve the same task.
I hope I understood what you were asking. You mention an "array of dictionaries" but you don't actually have an array of dictionaries anywhere in the code you've posted.
As far as I can tell, you are asking how to find all the entries in the found array for which itemName equals the filterItemName property.
If so, all you should need to do is:
let foundItems = found.filter { $0.itemName == filterItemName }
That's it.
Some other ideas:
If you want to search for items where filterItemName is contained in the itemName, you could do something like this:
let foundItems = found.filter { $0.itemName.contains(filterItemName) }
You could also make use of the lowercased() function if you want to do case-insensitive search.
You could also return properties of your found elements into an array:
let foundIds = found.filter { $0.itemName == filterItemName }.map { $0.itemId }
Sort array of dictionary using the following way
var dict:[[String:AnyObject]] = sortedArray.filter{($0["parentId"] as! String) == "compareId"}
The filter function loops over every item in a collection, and returns a collection containing only items that satisfy an include condition.
We can get single object from this array of dictionary , you can use the following code
var dict = sortedArray.filter{($0["parentId"] as! String) == "compareId"}.first
OR
let dict = sortedArray.filter{ ($0["parentId"] as! String) == "compareId" }.first
Local search filter using predicate in array of dictionary objects
with key name this code use for both swift3 and swift4,4.1 also.
func updateSearchResults(for searchController:
UISearchController) {
if (searchController.searchBar.text?.characters.count)! > 0 {
guard let searchText = searchController.searchBar.text,
searchText != "" else {
return
}
usersDataFromResponse.removeAll()
let searchPredicate = NSPredicate(format: "userName
CONTAINS[C] %#", searchText)
usersDataFromResponse = (filteredArray as
NSArray).filtered(using: searchPredicate)
print ("array = \(usersDataFromResponse)")
self.listTableView.reloadData()
}
}
Here I use CoreData And I have Array of Dictionary .
Here I am filter the key paymentToInvoice that value is invoice array then key invoiceToPeople that key contain People Dictionary then I search FirstName, lastName, Organization multiple key contain searchText.
I hope it's helps Please try this Thank You
var searchDict = dict.filter { (arg0) -> Bool in
let (key, value) = arg0
for paymentInfo in (value as! [PaymentInfo]){
let organization = (Array((value as! [PaymentInfo])[0].paymentToInvoice!)[0] as! InvoiceInfo).invoiceToPeople?.organization
let firstName = (Array((value as! [PaymentInfo])[0].paymentToInvoice!)[0] as! InvoiceInfo).invoiceToPeople?.firstName
let lastName = (Array((value as! [PaymentInfo])[0].paymentToInvoice!)[0] as! InvoiceInfo).invoiceToPeople?.lastName
return organization?.localizedStandardRange(of: searchText) != nil || firstName?.localizedStandardRange(of: searchText) != nil || lastName?.localizedStandardRange(of: searchText) != nil
}
return true
}

Swift: If let statement failing to handle empty array

I have an application that is using the Foursquare API to download JSON data. I am using NSURLSession and the dataTaskWithRequest with completion block method to fetch the data. I am getting the data fine but sometimes a nested array named groups can be empty. And when I am parsing through the JSON like below, for some reason my conditional statement is not handling the empty array like I am expecting it should. Instead of evaluating the array as empty and proceeding to the "else" portion of the if let...else statement if instead throughs a runtime error stating: index 0 beyond bounds of empty array
if let response: NSDictionary = data["response"] as? [String: AnyObject],
groups: NSArray = response["groups"] as? NSArray,
// Error here \|/ (sometimes groups array is empty)
dic: NSDictionary = groups[0] as? NSDictionary,
items: NSArray = dic["items"] as! NSArray {
}
else {
// Never gets here. Why?
// IF the groups array is empty, then the if let should return
// false and drop down to the else block, right?
}
I am relatively new to Swift, can someone tell me why this is happening and what I can do to fix this? Thanks
You have to check explicitly outside an if letstatement if the array is empty, because
An empty array is never an optional
if let response = data["response"] as? [String: AnyObject], groups = response["groups"] as? NSArray {
if !groups.isEmpty {
if let dic = groups[0] as? NSDictionary {
items = dic["items"] as! NSArray
// do something with items
println(items)
}
}
} else ...
You can omit all type annotations while downcasting a type
However, you can perform the check with a where clause, this works in Swift 1.2 and 2
if let response = data["response"] as? [String: AnyObject],
groups = response["groups"] as? [AnyObject] where !groups.isEmpty,
let dic = groups[0] as? NSDictionary,
items = dic["items"] as? NSArray {
// do something with items
println(items)
} else {...
Use && operator between statments.
For example this won't crash:
var a = 4;
var c = 5;
var array = Array<Int>();
if a > 2 && c > 10 && array[0] != 0 {
}
Make sure the array is not empty before trying to access it:
if let response: NSDictionary = data["response"] as? [String: AnyObject],
let groups: NSArray = response["groups"] as? NSArray where groups.count > 0 {
if let dic: NSDictionary = groups[0] as? NSDictionary,
let items: NSArray = dic["items"] as? NSArray {
// Do something..
return // When the 2nd if fails, we never hit the else clause
}
}
// Do logic for else here
...

Swift array of dictionaries with multiple types without using NSDictionary in iOS

I have been working with an array of dictionaries in Swift (version 7 beta 4) using NSDictionary. The array is a collection of fmdb results which contain differing data types. I would like to end up with an array of native swift dictionaries in order to use the filter functionality of swift collection types. The following is a snippet of the query function I'm using the create the array:
class func query(sql:String) -> [NSDictionary] {
let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask, true)
let docsDir = dirPaths[0] as String
let databasePath:String = docsDir.stringByAppendingPathComponent("myDB.db")
let db = FMDatabase(path: databasePath as String)
var resultsArray:[NSDictionary] = []
if db.open() {
let results:FMResultSet? = db.executeQuery(sql, withArgumentsInArray: nil)
while (results?.next() == true) {
resultsArray.append(results!.resultDictionary()) //appending query result
}
db.close()
} else {
print("Error: \(db.lastErrorMessage())")
}
return resultsArray
}
I tried using AnyObject such as:
class func query(sql:String) -> [[String:AnyObject]] {
let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask, true)
let docsDir = dirPaths[0] as String
let databasePath:String = docsDir.stringByAppendingPathComponent("Kerbal.db")
let db = FMDatabase(path: databasePath as String)
var resultsArray:[[String:AnyObject]] = []
if db.open() {
let results:FMResultSet? = db.executeQuery(sql, withArgumentsInArray: nil)
while (results?.next() == true) {
resultsArray.append(results!.resultDictionary() as! Dictionary<String,AnyObject>)
}
db.close()
} else {
print("Error: \(db.lastErrorMessage())")
}
return resultsArray
}
however I can't use the filter such as resultsArray.filter({$0["fieldName"] == "part"}) with AnyObject.
My question: Is it possible to create this array with native Swift dictionaries even though the dictionaries have different types? Could the new protocol extension be used on collection type to solve this problem?
Any suggestions are appreciated.
If you don't know which objects you are dealing with you have to use filter like so:
resultsArray.filter{ ($0["fieldName"] as? String) == "part"}
So you optionally cast the value to the desired type and compare it.
Note: I'm using the trailing closure syntax.
As suggestion I would use a tuple of different arrays which hold dictionaries:
// some dummy values
let resultsArray: [[String : AnyObject]] = [["Hi" : 3], ["Oh" : "String"]]
var result = (doubles: [[String : Double]](), strings: [[String : String]]())
// going through all dictionaries of type [String : AnyObject]
for dict in resultsArray {
// using switch and pattern matching to cast them (extensible for more types)
// you have to up cast the dictioanary in order to use patten matching
switch dict as AnyObject {
case let d as [String : Double]: result.doubles.append(d)
case let s as [String : String]: result.strings.append(s)
default: fatalError("unexpected type")
}
}
return result
You should probably adopt the Equatable protocol, not all AnyObjects are such in fact, and so no comparison and thereafter filtering may be done.

String not convertible to DictionaryIndex<String, AnyObject>

I have a function that loops through an array that contains either Strings or Dictionaries and sets values for a new Dictionary of type [String:AnyObject], using values from the original array as keys. sections is an array of custom Section objects and its method getValue always returns a String.
func getSectionVariables() -> [String:AnyObject] {
var variables = [String:AnyObject]()
for section in sections {
if let name = section.name as? String {
variables[name] = section.getValue()
} else if let name = section.name as? [String:String] {
for (k, v) in name {
if variables[k] == nil {
variables[k] = [String:String]()
}
if var dict = variables[k] as? [String:String] {
dict[v] = section.getValue() //This works, but of course copies variables by value and doesn't set variables[k]
}
(variables[k] as? [String:String])![v] = section.getValue() //Can't get this line to compile
}
}
}
return variables
}
If I try to cast variables[k] as a [String:String], the compiler insists that String is not convertible to DictionaryIndex<String, AnyObject>. I'm not sure why downcasting isn't working and why the compiler thinks I'm trying to use the alternate dictionary subscript syntax.
Note that this project is in Swift 1.1.
The general problem is that a "cast expression" does not yield an "l-value" and therefore cannot be assigned to. For example, consider the following simplified version of the code:
var x = "initial"
var y : AnyObject? = x
(y as? String) = "change"
You will get an error on the last line, because you cannot assign to a "cast expression".

Resources