How to remove duplicate keys from dictionary? - swift

I'm using Xcode 13.4. I'm working on a dictionary based app. I've an issue with dictionary that I've thousands of keys in dictionary. Now I've an error
Thread 1: Fatal error: Dictionary literal contains duplicate keys
How to get ride of this error? I've tried many answers from StackOverFlow but i can't get solution about my lengthy dictionary.
let dict : [String:String] = [
"ik4":"一",
"sioh8":"一",
"cik4": "七",
"ding1": "丁",
"diong7": "丈",
"diong5":"丈",
.........]

Each dictionary key MUST be unique. Its your responsibility if you are trying to create manually dictionary, Keys should be unique.
As such no api to remove duplicate keys from dictionary.

If it's important to check also the values of the duplicate keys you have to do it manually.
If not an easy way to remove the duplicate keys is to make the dictionary a JSON string and decode end re-encode the JSON.
Copy the entire dictionary literal and paste it into a Playground
Replace [] with {} and wrap the literal in """ like
let dict = """
{
"ik4":"一",
"sioh8":"一",
"cik4": "七",
"ding1": "丁",
"diong7": "丈",
"diong5":"丈",
"cik4": "七"
}
"""
Run this code
do {
let input = try JSONDecoder().decode([String:String].self, from: Data(dict.utf8))
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let output = try encoder.encode(input)
let string = String(data: output, encoding: .utf8)!
print(string)
} catch {
print(error)
}
Copy the result in the Console between {} and paste it into your project.

Related

Remove string literals from elements inside an array

Please guys i need to parse a string to look like these in swift
"[{"question":9, "answer":25}", "question\":10, "answer":27}]"
where the index and value are dynamically gotten from a loop. I was able to get to these
["{\"question\":9, \"answer\":25}", "{\"question\":10, \"answer\":27}", "{\"question\":11, \"answer\":29}", "{\"question\":12, \"answer\":33}", "{\"question\":13, \"answer\":37}"]
so i have tried this
for i in 0..<answersForQuestionInPage.count{
let questions = answersForQuestionInPage[i] as Answer
do {
let data = try JSONEncoder().encode(questions)
// 2
let string = String(data: data, encoding: .utf8)!
answers.append(string)
print("This is the main value \(string)")
} catch{
}
}
this still gives me an array with this format
["{\"question\":9, \"answer\":25}", "{\"question\":10,
\"answer\":27}", "{\"question\":11, \"answer\":29}",
"{\"question\":12, \"answer\":33}", "{\"question\":13,
\"answer\":37}"]
with the object
"{\"question\":9, \"answer\":25}"
still wrapped in a string liteal " " what i want is for this return array to be in this format
[{"question":9, "answer":25}, {"question":10,
"answer":27}, {"question":11, "answer":29},
"{"question":12, "answer":33}, {"question":13,
"answer":37}]
I didn't understand the whole thing, but you said you need to parse the String, but I think you meant JSON. So, you can do it like this and get the values. Do let me know if it is what you needed, otherwise please add clarity in your question and I will edit and update my answer accordingly.
struct Quiz: Decodable {
let question, answer: Int
}
private func fetchQuizzes() {
//After getting the data from API, you can do this
guard let quiz = try? JSONDecoder().decode([Quiz].self,from: data) else { print("Unable to parse"); return }
print(quiz)
print(quiz.first?.answer) //First Answer
}
Just like Rob said before, you have a JSON here.
Using Robs code you decode the given JSON and create an array of Quiz objects (Robs struct).
You can now work with that array and transform it to your needs.

Ambiguous reference to member 'decode(_:forKey:)' swift4

I am parsing a response that needs to be transformed from a dictionary on the server (which is a legacy data format) - to simply an array of strings on the client side. Therefore I am wanting to decode the key called 'data' as a dictionary, so i can iterate through the keys and create an array of strings on the client side.
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
do {
let some_data_dictionary = try values.decode([String:Any].self, forKey: CodingKeys.data)
for (kind, values) in some_data_dictionary {
self.data_array.append(kind)
}
} catch {
print("we could not get 'data' as [String:Any] in legacy data \(error.localizedDescription)")
}
}
The error I am getting is: Ambiguous reference to member 'decode(_:forKey:)'
Looks like Swift 'Codable' cant support Any or use of [String:Any], so using this post here Swift 4 decodable nested json with random key attributes
I was able to make a struct for a class I wouldn't use called LegacyData, and then unpack the keys into an array of strings
do
{
let legacy_data = try values.decode([String:LegacyData].self, forKey: CodingKeys.data)
self.array = Array(legacy_data.keys)
}
catch
{
print("no legacy_data \(error) \n")
}

Syntax about try and "as?" "as!"

I read a snippet from this POST but have not quite understood.
https://www.hackingwithswift.com/example-code/system/how-to-parse-json-using-nsjsonserialization
I am confused on some syntax in the following snippets.
In the following, I am not knowing why try goes here, it is strange syntax to me. Any information about try and as! for this expression?
let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
In the following, I am not knowing what is as? [String] doing?
if let names = json["names"] as? [String] {
I know this question might be fundamental, I just need a keyword for me to search the related anwser, thanks. Here is my whole code block.
// define a string
let str = "{\"names\": [\"Bob\", \"Tim\", \"Tina\"]}"
// convert the string into NSUTF8StringEncoding
let data = str.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
// put the following statements in try catch block
do {
// not knowing why try goes here, strange syntax to me.
// any higher conception about try and as! for this expression
let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
// json should be an object(dictionary, hash) use the key "names" to store string array into names
// not knowing what is `as? [String]` doing? any keyword for this syntax?
if let names = json["names"] as? [String] {
print(names)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
// not knowing why try goes here, strange syntax to me. what if it fails? as! is what syntax, any keyword to search for it?
The try here is to basically to try to perform the function on that line. If it fails it will go to the catch. Which in this case will just print("Failed to load: \(error.localizedDescription)")
// json should be an object(dictionary, hash) use the key "names" to store string array into names // not knowing what is as? [String] doing? any keyword for this syntax?
The line that you are confuse about is performing a if else on the json object. It check for a key that is name and also want to ensure that its value is a String therefore the as?. In this case if the key name does not exist it will not met the condition. If the value of name is not a String it will not met the condition.
Like what #Martin R mention in the comments, you should read up more on Type Casting in Swift. This should help you. Explanation with some example if you have trouble with Apple Documentation.
As for try catch it is actually use in other languages as well not just Swift. You can read up more here.

Alamofire + swiftyJSON array parsing

When I got JSON {"categories": "[\"a/b/c\",\"d\"]"} from server,
Then How can I parse it to ["a/b/c", "d"]
When I using .arrayValue, then it always return []
You have an array represented as raw JSON string within your JSON. You have three options:
Try to manually scan through replacing occurrences of \" (which you'll have to confusingly escape both the backslash character and the quotation mark encountered with a backslash when you write your code, e.g. "\\\"") before you parse the JSON. To do this, you'd probably use responseData rather than responseJSON, do your manual replacement of characters, and then manually parse it yourself with NSJSONSerialization. The downside here is that you might want to check for other escaped characters, too (e.g. what if the nested JSON was pretty printed, then you might have \n or even \t in there, too, which you'd want to convert as well).
Manually run JSON parsing again for each of those values that contain a JSON string in the responseObject. If you're stuck with the existing JSON, I think this is probably safest.
For example, if the raw json was really just:
{"categories": "[\"a/b/c\",\"d\"]"}
Then you could do something like:
Alamofire.request(request)
.responseJSON { response in
guard response.result.error == nil else {
print(response.result.error!)
return
}
do {
if let dictionary = response.result.value as? [String: String],
let categoryData = dictionary["categories"]?.dataUsingEncoding(NSUTF8StringEncoding),
let categories = try NSJSONSerialization.JSONObjectWithData(categoryData, options: []) as? [String]
{
let result = ["categories" : categories]
print(result)
}
} catch {
print(error)
}
}
Then the result would be:
["categories": ["a/b/c", "d"]]
Fix the web service that's generating this JSON so that it's not doing this silliness. This is probably the best solution, though it will take some detective work to figure out why/how the web service put JSON inside JSON. I know that you say you can't change the server side, but you really should escalate this to someone on the server team to fix this. It's silly to write client code to work around some mistake (either a mistake in the server code itself, or how the server was provided the data in the first place).
First up, the response you're getting from the server is a String and not Array. Hence you'll get empty array when you do .arrayValue . If you want to convert the string into an array, first you need to remove the "[" & "]" and then you need to do the componentsSeparatedByString with string as "," for the resultant string. You can use the following code snippet to do that:
let str = "[\"a/b/c\",\"d\"]"
let filteredString = String (str.characters.filter { $0 != "[" && $0 != "]"})
print(filteredString)
let filteredStringArray = filteredString.componentsSeparatedByString(",")
print(filteredStringArray)
HTH :)

Storing multiple values in Keychain using Locksmith

Can you store multiple key/value pairs under one forUserAccount?
try? Locksmith.saveData(["access_token" : access_token], forUserAccount: "UserData")
try? Locksmith.saveData(["email" : email!], forUserAccount: "UserData")
try? Locksmith.saveData(["markets" : market], forUserAccount: "UserData")
This code fails when looking for "email.". The only way I can do this is to create multiple forUserAccount strings: UserData, UserData1, UserData3, etc. I have tried updateData with no luck. If forUserAccount has to be unique, what is it for? It seems unnecessary if that's the case.
let userIDNumber = userID as NSNumber
let userIDString : String = userIDNumber.stringValue
let keychainData = [_keychainEmailKey: email, _keychainPasswordKey: password, _keychainUserIDKey: userIDString]
Locksmith.updateData(keychainData, forUserAccount: email)
Where _keychainEmailKey, etc are constants for keys. So forUserAccount should be unique, probably it is overriding previous values.
Just like #nyekimov's answer alludes to, a dictionary of values can be stored for an account. To clarify, here's a code snippet:
do
{
try Locksmith.saveData(["user": "notMyRealUsername", "password":"123456"], forUserAccount: "myUserAccount") //store it
let dictionary = Locksmith.loadDataForUserAccount("myUserAccount") //load it
print(dictionary) //let's see what we've got
}
catch
{
// handle errors here
}
Use try Locksmith.updateData(["user": "notMyRealUsernameEither", "password":"123456"], forUserAccount: "myUserAccount") when an account exists.