Proper way to get swift dictionary item index - ios

I have dictionary in my app written in Swift
var cities: [String: String] = ["":"not specified", "ny":"New York", "la":"Los Angeles", "sf":"San Francisco"]
I use this dictionary to build pickerView. When user selects one of this items - city code is saving to device memory.
When user opens app next time I want pickerView to show saved city. How should i do it?

You can find the index of a key in a dictionary with higher-order functions:
let desiredKey = "ny"
let pos = x.enumerate().filter { (pair: (index: Int, element: (String, String))) -> Bool in
return pair.element.0 == desiredKey
}.map { (pair: (index: Int, element: (String, String))) -> Int in
return pair.index
}
pos is an Optional(Int) containing the position of "ny" in the current iteration order of the dictionary.
Store the value of the key somewhere (say, in user defaults), then retrieve it, get the index of the key-value pair using the code above, and pass to selectedRowInComponent: of your picker view.
Note: The problem with using a dictionary alone as a backing for your picker is that the order is not specified explicitly. Adding or removing keys may change the order of existing keys. In addition, existing keys may not end up in places that you want them to be - for example, your "" - "not specified" pair may end up in the middle of the picker.
To fix this problem, keep a separate array of city keys in the proper order that you wish to follow. Use this array to decide the picker row in which a city is to be displayed, and look up the actual city using the dictionary.

You can use NSUserDefaults to insure the persistency of your cityCode.
Copy an paste this sample in a playground.
var cities: [String: String] = ["":"not specified", "ny":"New York", "la":"Los Angeles", "sf":"San Francisco"]
let cityCodeIndexKey="cityCodeIndexKey"
/**
Selects and saves the cityCode
- parameter cityCode: a cityCode String
- returns: true if the code exists.
*/
func selectCity(cityCode:String)->Bool{
if let _ = cities.indexForKey(cityCode){
NSUserDefaults.standardUserDefaults().setObject(cityCode, forKey:cityCodeIndexKey)
NSUserDefaults.standardUserDefaults().synchronize()
return true
}else{
return false
}
}
selectCity("ny") // Returns true
selectCity("pr") // Returns false
NSUserDefaults.standardUserDefaults().valueForKey(cityCodeIndexKey) // returns "ny"

Get key,
var cityLongName <- user selected one
var key = dict.first((key,value) In{
If (value == cityLongName ){
return true
}else{
return false
}
}).key!
Get values ,
var value = dict[“city sort name”]

Related

Cast data of type Any to Array of strings in Swift

So in working on an app using swift and firebase but i have an issue, i have a table view where i add in the data as dates but when i retrieve them, i want them ordered in a way by the time i added them in. so last item will be at the last index.
Basically i retrieve a data block as type any
Optional({
"-Ly6Njj9vNH_T9umxDwS" = "01-08-2020";
"-Ly6U4vzEAGvIC0Yblnl" = "12-08-2020";
"-Ly6U7_91jW5tajpX7k0" = "11-08-2020";
"-Ly6UG6HYqTMq1NaRehI" = "10-08-2020";
})
so here you can see they are numbered correclty frim top to bottom but when i try to put them into an array using
data as? [String]
it doesnt work and i can do
data as [String: String]
but that doesnt keep the order, i know i can add time stamps and such but if i have the data correctly formatted aleady but in a type of Any, there must be a way to cast it into an array
A dictionary has no real order; the order of printout is not meaningful and can change. We can reproduce this without reference to firebase:
let d : NSDictionary = [
"-Ly6Njj9vNH_T9umxDwS" : "01-08-2020",
"-Ly6U4vzEAGvIC0Yblnl" : "12-08-2020",
"-Ly6U7_91jW5tajpX7k0" : "11-08-2020",
"-Ly6UG6HYqTMq1NaRehI" : "10-08-2020"
]
print(d)
//{
// "-Ly6Njj9vNH_T9umxDwS" = "01-08-2020";
// "-Ly6U4vzEAGvIC0Yblnl" = "12-08-2020";
// "-Ly6U7_91jW5tajpX7k0" = "11-08-2020";
// "-Ly6UG6HYqTMq1NaRehI" = "10-08-2020";
//}
let d2 = d as! [String:String]
print(d2)
// ["-Ly6Njj9vNH_T9umxDwS": "01-08-2020",
"-Ly6U4vzEAGvIC0Yblnl": "12-08-2020",
"-Ly6UG6HYqTMq1NaRehI": "10-08-2020",
"-Ly6U7_91jW5tajpX7k0": "11-08-2020"]
So if you want an ordered list of pairs you must use an array and you must order it yourself somehow, presumably using the timestamp. But as a comment says, that order is the order of the keys.
let arr = Array(d2).sorted {$0.key < $1.key}
print(arr)
//[(key: "-Ly6Njj9vNH_T9umxDwS", value: "01-08-2020"),
// (key: "-Ly6U4vzEAGvIC0Yblnl", value: "12-08-2020"),
// (key: "-Ly6U7_91jW5tajpX7k0", value: "11-08-2020"),
// (key: "-Ly6UG6HYqTMq1NaRehI", value: "10-08-2020")]
If all you want is the date strings, just map that down to its values. The order is preserved because these are now arrays.
let arr2 = arr.map {$0.value}
print(arr2)
// ["01-08-2020", "12-08-2020", "11-08-2020", "10-08-2020"]

Accessing Dictionary element for a tableview

I have passed a dictionary to a second view controller and assigned it to an array, I thought I could access the data easier this way:
var myAlerts: NSDictionary!
The dictionary has three elements for each: Id (which I don't care about), alertDate, and alertNote.
I'm trying to get these elements into a tableView but struggling with this.
I thought about just moving it into two arrays and accessing it that way, cumbersome but it at least gets me further down the road so to speak.
Here is the raw data from the dictionary AFTER it was past to the second controller:
{
alerts = (
{
alertDate = "2017-07-16";
alertNote = "Rob is the worlds greatest friend";
id = 2;
},
{
alertDate = "2017-07-17";
alertNote = "This is a test of the emergency system";
id = 1;
}
);
}
When I tried to move the values into two arrays with this:
func CreateArray() {
for i in 0...myAlerts.count {
alertsDate[i] = myAlerts["alerts"]["alertDate"]
alertsNote[i] = myAlerts["alerts"]["alertNote"]
}
}
I get the proverbial Type Any? has no subscript members.
Any help would be appreciated.
myAlerts with that data is now a dictionary containing an array of dictionaries. (so top level is a dictionary, with one key/value pair which is of type array of [String:Any] objects).
Since a dictionary value is of type Any, it can't infer in this case what the type of the value for the key alerts is. So you have to try cast it to a specific type first, in this case an array of dictionaries, i.e. [[String:Any]]
So this should get rid of your error:
func CreateArray() {
for i in 0...myAlerts.count {
let alertArray = myAlerts["alerts"] as! [[String:Any]]
alertsDate.append(alertArray[i]["alertDate"] as! String)
alertsNote.append(alertArray[i]["alertNote"] as! String)
}
}
Note: I had to change alertsDate and alertsNote arrays to using append as in my demo code i had no existing items in the array and using and index would have caused an error.

Update Array [[String: [Dictionary]]] when valueChanged in Firebase

var chatMessages = [[String: ChatMessage]]()
Firebase Chat Messages structure is like this.
-Kjws99ol6qjFt7ET9C
content: "Hehd"
displayName: "John Doe"
fileLength: 0
fileUrl: ""
fromID: "5904ee8cfa"
isRead: false
messageStatus: 2
messageType: "normal"
timestamp: 1494596232
Now on childAdded I'm appending the new message like this
weakSelf.chatMessages.append(newMessage)
//Kjws99ol6qjFt7ET9C - This is the threadID which is stored in String and below value is stored in ChatMessage
But after message isRead by user it value changes and that is identified by childChanged so in childChanged change how to update my Array correctly?
On the childChanged event, the app is passed a snapshot of the updated child, with the (in this case) key being Kjws99ol6qjFt7ET9C and the value being the child node data.
To update the array, find which index in the chatMessages array corresponds to that key and update the value accordingly.
To find it in the array you've set up, which is an array of [String: ChatMessage] dictionaries do the following
let searchKey = "Kjws99ol6qjFt7ET9C"
let index = chatMessages.map( {$0.keys.first!} ).index(of: searchKey)
Once you have the index, you can then update the element in the array with the new data.
{$0.keys.first!} - compiles all of the keys in the chatMessage array into an array
index(of: searchKey) - finds the index of the searchKey we are looking for
Then you can
chatMessage[index] = updated data
If you need any additional code, let me know.
However, I would strongly encourage changing the model to store a ChatMessage class (or struct) in the array
class ChatMessage {
var fbKey = "" // the key like Kjws99ol6qjFt7ET9C
var content = "" // like "Hehd"
var displayName "" // "John Doe"
}
var chatMessages = [ChatMessage]()
it will be easier to maintain and the array search is simplified and faster.
With this use case, to find a specific index do this
let searchKey = "Kjws99ol6qjFt7ET9C"
let index = chatMessages.index(where: { $0.fbKey == searchKey} )
I'm confused as to why you are storing the dictionary inside an array. It looks to me like
var chatMessages = [String: ChatMessage]()
would satisfy your model as each message push id (e.g. -Kjws99ol6qjFt7ET9C) would be unique from the database, so you could have a valid Dictionary by storing this push id as the key.
When you receive a childChanged event, you can then quickly locate the message that has changed by looking up the snapshot.key in the dictionary and updating it.

Access Parse Array Elements

I have an array column in Parse that is called homeAddress. Inside it is the following value for a single row:
["111111","222222"]
I wish to access this in swift. I created a local array called homeAddress of String type. How can I store the first element in the Parse column (i.e. 111111) in the first element holder in local array i.e. homeAddress[0]? And then I need to store the 222222 in the homeAddress[1].
So what you want is load this data from parse. You can just call a Parse Query get the results and work with them:
let query = PFQuery(className:"ClassName")
query.findObjectsInBackgroundWithBlock {
(results, error) -> Void in
if (error == nil) {
for object in results!{
let homeAddress = object["homeAddress"] as! NSArray
}
}else {
print(error?.userInfo)
}
}
One way would be to trim first and last character and just separate values by comma:
var test = "[\("111111"),\("222222")]"
var trimmedStringRange = Range<String.Index>(start: test.startIndex.successor(), end: test.endIndex.predecessor())
var homeAddress = test.substringWithRange(trimmedStringRange).componentsSeparatedByString(",")
If your local array is empty, you can add your values from the parse array like this (after getting the array from Parse):
localHomeAddress.append(homeAddress)
This will put all values from homeAddress array in localHomeAddress array.
If you use a splice, you can ensure you are adding the values from index 0:
localHomeAddress.insertContentsOf(homeAddress, at:0)

Search Array of Dictionaries for Value in Swift

I'm new to Swift and taking a class to learn iOS programming. I find myself stumped on how to search in an array of dictionaries for a string value and dump the string value into an array. This is taken from my Xcode playground.
I'm trying to figure out how to:
1) search through an array of dictionaries
2) dump the results of the search into an array (which I've created)
These are the character dictionaries.
let worf = [
"name": "Worf",
"rank": "lieutenant",
"information": "son of Mogh, slayer of Gowron",
"favorite drink": "prune juice",
"quote" : "Today is a good day to die."]
let picard = [
"name": "Jean-Luc Picard",
"rank": "captain",
"information": "Captain of the USS Enterprise",
"favorite drink": "tea, Earl Grey, hot"]
This is an array of the character dictionaries listed above.
let characters = [worf, picard]
This is the function I'm trying to write.
func favoriteDrinksArrayForCharacters(characters:Array<Dictionary<String, String>>) -> Array<String> {
// create an array of Strings to dump in favorite drink strings
var favoriteDrinkArray = [String]()
for character in characters {
// look up favorite drink
// add favorite drink to favoriteDrinkArray
}
return favoriteDrinkArray
}
let favoriteDrinks = favoriteDrinksArrayForCharacters(characters)
favoriteDrinks
I would be grateful for any assistance on how to move forward on this. I've dug around for examples, but I'm coming up short finding one that's applicable to what I'm trying to do here.
Inside the loop, you need to fetch the "favorite drink" entry from the dictionary, and append it to the array:
for character in characters {
if let drink = character["favorite drink"] {
favoriteDrinkArray.append(drink)
}
}
Note, the if let drink = guards against the possibility there is no such entry in the array – if there isn't, you get a nil back, and the if is checking for that, only adding the entry if it's not nil.
You might sometimes see people skip the if let part and instead just write let drink = character["favorite drink"]!, with an exclamation mark on the end. Do not do this. This is known as "force unwrapping" an optional, and if there is ever not a valid value returned from the dictionary, your program will crash.
The behavior with the first example is, if there is no drink you don't add it to the array. But this might not be what you want since you may be expecting a 1-to-1 correspondence between entries in the character array and entries in the drinks array.
If that's the case, and you perhaps want an empty string, you could do this instead:
func favoriteDrinksArrayForCharacters(characters: [[String:String]]) -> [String] {
return characters.map { character in
character["favorite drink"] ?? ""
}
}
The .map means: run through every entry in characters, and put the result of running this expression in a new array (which you then return).
The ?? means: if you get back a nil from the left-hand side, replace it with the value on the right-hand side.
Airspeed Velocity's answer is very comprehensive and provides a solution that works. A more compact way of achieving the same result is using the filter and map methods of swift arrays:
func favoriteDrinksArrayForCharacters(characters:Array<Dictionary<String, String>>) -> Array<String> {
// create an array of Strings to dump in favorite drink strings
return characters.filter { $0["favorite drink"] != nil }.map { $0["favorite drink"]! }
}
The filter takes a closure returning a boolean, which states whether an element must be included or not - in our case, it checks for the existence of an element for key "favorite drink". This method returns the array of dictionaries satisfying that condition.
The second step uses the map method to transform each dictionary into the value corresponding to the "favorite drink" key - taking into account that a dictionary lookup always returns an optional (to account for missing key), and that the filter has already excluded all dictionaries not having a value for that key, it's safe to apply the forced unwrapping operator ! to return a non optional string.
The combined result is an array of strings - copied from my playground:
["prune juice", "tea, Earl Grey, hot"]
let drinks = characters.map({$0["favorite drink"]}) // [Optional("prune juice"), Optional("tea, Earl Grey, hot")]
or
let drinks = characters.filter({$0["favorite drink"] != nil}).map({$0["favorite drink"]!}) // [prune juice, tea, Earl Grey, hot]
It may help you
var customerNameDict = ["firstName":"karthi","LastName":"alagu","MiddleName":"prabhu"];
var clientNameDict = ["firstName":"Selva","LastName":"kumar","MiddleName":"m"];
var employeeNameDict = ["firstName":"karthi","LastName":"prabhu","MiddleName":"kp"];
var attributeValue = "karthi";
var arrNames:Array = [customerNameDict,clientNameDict,employeeNameDict];
var namePredicate = NSPredicate(format: "firstName like %#",attributeValue);
let filteredArray = arrNames.filter { namePredicate.evaluateWithObject($0) };
println("names = ,\(filteredArray)");
Use the following code to search from NSArray of dictionaries whose keys are ID and Name.
var txtVal:NSString
let path = NSBundle.mainBundle().pathForResource(plistName, ofType: "plist")
var list = NSArray(contentsOfFile: path!) as [[String:String]]
var namePredicate = NSPredicate(format: "ID like %#", String(forId));
let filteredArray = list.filter { namePredicate!.evaluateWithObject($0) };
if filteredArray.count != 0
{
let value = filteredArray[0] as NSDictionary
txtVal = value.objectForKey("Name") as String
}
i have array of customer ,each customer having name,phone number and other stubs .so i used the below code to search by phone number in the array of dictionary in search bar
for index in self.customerArray
{
var string = index.valueForKey("phone")
if let phoneNumber = index.valueForKey("phone") as? String {
string = phoneNumber
}
else
{
string = ""
}
if string!.localizedCaseInsensitiveContainsString(searchText) {
filtered.addObject(index)
searchActive = true;
}
}

Resources