Saving more than one value to a Dictionary key in a loop with an Array - swift

I have this block of code
//start of the loop
if let objects = objects as? [PFObject] {
for object in objects {
//saving the object
self.likerNames.setObject(object["fromUserName"]!, forKey: saveStatusId!)
}
}
likerNames is an NSMutableArray declared earlier, saveStatusId is a string I also declared and saved earlier (It's just an objectId as a String), and object["fromUserName"] is an object returned from my query (not shown above).
Everything is working fine as it is but my query sometimes returns more than one object["fromUserName"] to the same key which is saveStatusId. When this happens the value I have for that saveStatusId is replaced when I actually want it to be added to the key.
So want it to kind of look like this
("jKd98jDF" : {"Joe", "John"})
("ksd6fsFs" : {"Sarah"})
("payqw324" : {"Chris", "Sarah", "John"})
I know you can use Arrays but I'm not sure how I would go about that to get it to work in my current situation.
So my question would be how to I get my key (saveStatusId) to store more than one value of object["fromUserName"]?

Something like this could work
let key = saveStatusId!
let oldValue = self.likerNames.objectForKey( key ) as? [String]
let newValue = (oldValue ?? []) + [ object["fromUserName" ] ]
self.likerNames.setObject( newValue, forKey: key )
If likerNames has an array in slot[saveStatusId], append the new value, otherwise create an array and put that in the right slot

Related

How to fill Dictionary dynamically

I parse CSV file loaded into content: String variable. File has three values on each of its rows and I'd like to fill Dictionary using three keys (autID, autSName, autFName). I wrote code below
var valuesDict = Dictionary<String,Any>()
let dataRows = content.components(separatedBy: "\n")
for dataRow in dataRows {
let values = dataRow.components(separatedBy: ";")
if Int64(values[0]) != nil {
valuesDict.updateValue(Int64(values[0])!, forKey: "autID")
valuesDict.updateValue(values[1], forKey: "autSName")
valuesDict.updateValue(values[2], forKey: "autFName")
}
}
Parsing is working fine but dictionary contents values only from the last data line even if updateValue method is documented as "Updates the value stored in the dictionary for the given key, or adds a new key-value pair if the key does not exist." so I assumed that it will add all unique key-value combinations into valuesDict dictionary. What I'm doing wrongly here?
CSV is an array format, a line for the field names and subsequent lines representing the records.
You have to declare an array
var valuesArray = [Dictionary<String,Any>]()
let dataRows = content.components(separatedBy: "\n")
for dataRow in dataRows {
let values = dataRow.components(separatedBy: ";")
if let autID = Int64(values[0]) {
valuesArray.append(["autID": autID, "autSName": values[1], "autFName": values[2])
}
}
further you might drop the first line. And this is Swift, a custom struct is preferable over a primitive dictionary.

Filter An Array Of Arrays By A Value In Firestore Swift

I'm looking to filter an array of arrays by specific value of one of the keys located within each array. Each nested array is read in from Firestore.
As an object, each nested array would look like this:
struct Video {
var url: String
var submissionDate: Timestamp
var submittingUser: String
}
I'm reading it in like this:
videos = document.get("videos") as? [[String : Any]] ?? nil
So far so good, but when I filter it like this:
filteredVideos = videos.filter { $0[2].contains(self.userIdentification) }
I can't do it without getting the error "Reference to member 'contains' cannot be resolved without a contextual type," an error which I was unable to find any relevant information on SO about.
I have read that some people say "Don't use arrays in Firestore!" but this is a build requirement.
Anyone have any ideas? Basically just need all arrays within the array where userId == submittingUser.
Reference Article:
I tried the answer from here: How to filter out a Array of Array's but no luck for this situation.
It's actually an array of dictionaries, not an array of arrays. All you need to do is construct the right predicate for the filter.
This is basically what your Firestore data looks like:
let videos: [[String: Any]] = [
["url": "http://www...", "submittingUser": "user1"],
["url": "http://www...", "submittingUser": "user2"]
]
This is the user you're looking for:
let userIdentification = "user2"
This is the predicate for the filer:
let filteredVideos = videos.filter { (video) -> Bool in
if let submittingUser = video["submittingUser"] as? String,
submittingUser == userIdentification {
return true
} else {
return false
}
}
You can shorthand this down to a single line if you're okay with force-unwrapping the dictionary (if you're 100% certain every video will have a valid submittingUser value):
let filteredVideos = videos.filter({ $0["submittingUser"] as! String == userIdentification })

Write to Nested Dictionary (Swift 4)

I have declared a dictionary in Swift as so: var dict = [String: [String: [String]]]().
What I am trying to do now is to write to the nested dictionary. I have used both codes below, however, none of them work as the initial key does not exist:
dict["Test"]?["One"] = ["Failed"]
dict["Test"]!["One"] = ["Failed"]
What I am trying to do is to create a key for ["One"], much like how you can create a key for a normal dictionary using dict[key].
You need to instantiate every inner dictionary.
var dict = [String : [String : [String]]]()
dict["Test"] = [String : [String]]()
dict["Test"]?["One"] = ["Worked"]
print(dict)
Make sure to avoid force unwrapping.
dict is empty. There is no value for the "Test" key.
One option is to provide a default:
dict["Test", default: [:]]["One"] = ["A", "B"]
You can take this one step further:
dict["Test2", default: [:]]["Two", default: []].append("Hello")
That last line will work for any combination of the keys "Test2" and "Two" existing or not before that is used.

How to pass and get multiple URLQueryItems in Swift?

Ok, I am working in an iMessage app and am trying to parse more than 1 url query item from the selected message here- I have been successful getting/sending just 1 value in a query:
override func willBecomeActive(with conversation: MSConversation) {
// Called when the extension is about to move from the inactive to active state.
// This will happen when the extension is about to present UI.
if(conversation.selectedMessage?.url != nil) //trying to catch error
{
let components = URLComponents(string: (conversation.selectedMessage?.url?.query?.description)!)
//let val = conversation.selectedMessage?.url?.query?.description
if let queryItems = components?.queryItems {
// process the query items here...
let param1 = queryItems.filter({$0.name == "theirScore"}).first
print("***************=> GOT IT ",param1?.value)
}
}
When I just have 1 value, just by printing conversation.selectedMessage?.url?.query?.description I get an optional with that 1 value, which is good. But with multiple I cant find a clean way to get specific values by key.
What is the correct way to parse a URLQueryItem for given keys for iMessage?
When you do conversation.selectedMessage?.url?.query?.description it simply prints out the contents of the query. If you have multiple items then it would appear something like:
item=Item1&part=Part1&story=Story1
You can parse that one manually by splitting the string on "&" and then splitting the contents of the resulting array on "=" to get the individual key value pairs in to a dictionary. Then, you can directly refer to each value by key to get the specific values, something like this:
var dic = [String:String]()
if let txt = url?.query {
let arr = txt.components(separatedBy:"&")
for item in arr {
let arr2 = item.components(separatedBy:"=")
let key = arr2[0]
let val = arr2[1]
dic[key] = val
}
}
print(dic)
The above gives you an easy way to access the values by key. However, that is a bit more verbose. The way you provided in your code, using a filter on the queryItems array, is the more compact solution :) So you already have the easier/compact solution, but if this approach makes better sense to you personally, you can always go this route ...
Also, if the issue is that you have to write the same filtering code multiple times to get a value from the queryItems array, then you can always have a helper method which takes two parameters, the queryItems array and a String parameter (the key) and returns an optional String value (the value matching the key) along the following lines:
func valueFrom(queryItems:[URLQueryItem], key:String) -> String? {
return queryItems.filter({$0.name == key}).first?.value
}
Then your above code would look like:
if let queryItems = components?.queryItems {
// process the query items here...
let param1 = valueFrom(queryItems:queryItems, key:"item")
print("***************=> GOT IT ", param1)
}
You can use iMessageDataKit library. It makes setting and getting data really easy and straightforward like:
let message: MSMessage = MSMessage()
message.md.set(value: 7, forKey: "user_id")
message.md.set(value: "john", forKey: "username")
message.md.set(values: ["joy", "smile"], forKey: "tags")
print(message.md.integer(forKey: "user_id")!)
print(message.md.string(forKey: "username")!)
print(message.md.values(forKey: "tags")!)
(Disclaimer: I'm the author of iMessageDataKit)

Access data in Dictionary from NSCountedSet objects

I have an NSCountedSet consisting of String objects, if I iterate over the set and print it out, I see something like this:
for item in countedSet {
print(item)
}
Optional(One)
Optional(Two)
The countedSet is created from an Array of String objects:
let countedSet = NSCountedSet(array: countedArray)
If I print the array I see something like this:
["Optional(One)", "Optional(One)", "Optional(One)", "Optional(Two)", "Optional(Two)"]
Now, I want to use those counted strings to access data in a Dictionary, where the keys are String type and the values have the type [String : AnyObject]. If I print the dictionary, I see all the data.
However, if I use of the objects from the countedSet to access the dictionary, I always get a nil value back:
for item in countedSet {
let key = item as? String
let data = dictionary[key!] // Xcode forced me to unwrap key
print(data)
}
nil
nil
But
let key = "One"
let data = dictionary[key]
gives me the expected data.
What am I missing here, how can I access my data from the countedSet objects?
UPDATE: solved thanks to the comment from Martin R. The String objects were originally NSString objects (obtained from NSScanner), and I was casting them wrongly:
let string = String(originalString)
after I changed it to:
let string = (originalString as! String)
All works fine.