Create Dictionary from Array in Swift 5 - ios

I need to create a dictionary from array with custom type for first index of the array.
Sample array : ["ABC","ZYZ","123"]
Required result : [{"name" : "ABC", "type:"A"},{"name" : "ZYZ", "type:"B"},{"name" : "123", "type:"B"}]
Note type A for first index.
My code
for url in urlArray {
urlDict["name"] = url
}

You can do a map, and then individually change the type of the first dictionary:
var dicts = urlArray.map { ["name": $0, "type": "B"] }
dicts[0]["type"] = "A"
Seeing how all your dictionary keys are all the same, and that you are sending this to a server, a Codable struct might be a better choice.
struct NameThisProperly : Codable {
var name: String
var type: String
}
var result = urlArray.map { NameThisProperly(name: $0, type: "B") }
result[0].type = "A"
do {
let data = try JSONDecoder().encode(result)
// you can now send this data to server
} catch let error {
...
}

I suppose you can use a high order function such as map or reduce
Here is an example using reduce
var array = ["ABC","ZYZ","123"]
var result = array.reduce([[String: String]](), { (previous, current) -> [[String: String]] in
let type = previous.count == 0 ? "A" : "B"
let dictForCurrent = [
"name": current,
"type": type
]
return previous + [dictForCurrent]
})
print(result)
The result:
[["type": "A", "name": "ABC"], ["type": "B", "name": "ZYZ"], ["name":
"123", "type": "B"]]

Use reduce to convert array to dictionary:
let resultDict: [String: String]
= array.reduce(into: [:]) { dict, url in
dict["name"] = url
}
The result will look like:
[
"name": URL1,
"name": URL2
]

Use map(_:) to convert each element of the array to dictionary like so,
let arr = ["ABC","ZYZ","123"]
let result = arr.map { (element) -> [String:String] in
var dict = [String:String]()
dict["name"] = element
if let char = element.first {
dict["type"] = String(char)
}
return dict
}
print(result)

since you are concern about the index, my approach will be using enumerated() which gives out the index
let array = ["ABC","ZYZ","123"]
var results: [[String: String]] = []
for (i, content) in array.enumerated() {
let type: String = i == 0 ? "A" : "B"
results.append(["name": content, "type": type])
}
print(result)
// [["type": "A", "name": "ABC"], ["name": "ZYZ", "type": "B"], ["type": "B", "name": "123"]]

Related

Encode url string to Dictionary In my case: Some value have = inside

I'm trying to convert decode to dictionary from the below url encoded string. The normal method of doing so is given below. In my case it is not working. Also i need to remove any character like \u{05}
let params = str.components(separatedBy: "&").map({
$0.components(separatedBy: "=")
}).reduce(into: [String:String]()) { dict, pair in
if pair.count == 2 {
dict[pair[0]] = pair[1]
}
}
My url encoded string is
"id=sfghsgh=sbfsfhj&name=awsjdk_fs\u{05}"
I'm expecting result as
{
"id" = "sfghsgh=sbfsfhj",
"name" = "awsjdk_fs"
}
How it is possible to achive?
Piggyback on URLComponents:
var components = URLComponents()
components.query = "id=sfghsgh=sbfsfhj&name=awsjdk_fs"
components.queryItems
// => Optional([id=sfghsgh=sbfsfhj, name=awsjdk_fs])
let list = components.queryItems?.map { ($0.name, $0.value) } ?? []
// [("id", Optional("sfghsgh=sbfsfhj")), ("name", Optional("awsjdk_fs"))]
let dict = Dictionary(list, uniquingKeysWith: { a, b in b })
// ["name": Optional("awsjdk_fs"), "id": Optional("sfghsgh=sbfsfhj")]
If you need a [String: String] rather than [String: String?]:
let list = components.queryItems?.compactMap { ($0.name, $0.value) as? (String, String) } ?? []
// [("id", "sfghsgh=sbfsfhj"), ("name", "awsjdk_fs")]
let dict = Dictionary(list, uniquingKeysWith: { a, b in b })
// ["name": "awsjdk_fs", "id": "sfghsgh=sbfsfhj"]

Array of dictionary filter using predicate - swift3

I have array of dictionaries.
>[{"name": "John",
"address":
{"home": "addr1",
"work": "add2"}
},
{"name": "Anu",
"address": {"home": "addr1",
"work": "add2"}
}]
I am saving it to user default like this -
let personsData1 = ["name": "John", "address": {"home": "addr1", "work": "add2"}] as [String : Any]
let personsData2 = ["name": "Anu", "address": {"home": "addr1", "work": "add2"}] as [String : Any]
var persons = [personsData, personsData1]
UserDefaults.standard.set(forKey: "persons")
Retrieving it in another method and filter them on the basis of name.
let name = "John"
Getting below error
Cannot invoke 'filter' with an argument list of type '((Any?) -> Bool)'
Here is the code :-
func test () {
let personData1 = ["name": "John", "addresses": ["home":"addr1", "work": "addr2"]] as [String : Any]
let personData2 = ["name": "And", "addresses": ["home":"addr1", "work": "addr2"]] as [String : Any]
let persons = [personData1, personData2]
(UserDefaults.standard.set(persons, forKey: "persons")
print("Saved ----\(UserDefaults.standard.value(forKey: "persons"))")
if let savedPersons = UserDefaults.standard.value(forKey: "persons") {
let namePredicate = NSPredicate(format: "name like %#", name);
var filteredArray: [[String:Any]] = savedPersons.filter { namePredicate.evaluate(with: $0) }
print("names = \(filteredArray)")
}
}
If I try to filter like this -
let filteredArray = savedBrs.filter { $0["name"] == name }
getting different error -
Value of type 'Any' has no member 'filter'
With NSPredicate
let arr = [["name":"Rego","address":["one":"peek","two":"geelo"]],["name":"pppp","address":["one":"peek","two":"geelo"]]]
let neededName = "Rego"
let pre = NSPredicate(format: "name == %#",neededName)
let result = arr.filter { pre.evaluate(with:$0) }
print(result)
Without NSPredicate
let result = arr.filter { $0["name"] as? String == neededName }

How do i parse JSON, sort it in an array. Pull out the data and populate to a tableview?

i am trying to parse JSON into an array. Sort it according the highest appointment made. And then use that new array to populate it on leaderboard tableview.
I am using SwiftJson
Stuck on sorting into array
Would need to populate Name and the rest of the values in tableview in descending order.
Here are my snippets.
let jsonUrl = URL(string: url)
URLSession.shared.dataTask(with: jsonUrl!) { (data, response, error) in
guard let data = data else { return }
let jsonResult : JSON = JSON(data)
print(jsonResult)
} .resume()
Here are the output
{
"Activities" : {
"AHiHr9bzGXcN7pxvR68wulD9zqE3" : {
"Case Closed" : "2",
"Name" : "Tim Mac",
"Appointment Made" : "2",
"Prospects Met" : "2",
"Policy Servicing" : "2"
},
"gDKBlbeMsiUUFaASOLn6eOdCIrJ3" : {
"Case Closed" : "1",
"Name" : "Jane Simpson",
"Appointment Made" : "1",
"Prospects Met" : "1",
"Policy Servicing" : "1"
},
"W8uWoLf9qRX4a9BgXjLw5VZXjFu1" : {
"Case Closed" : "3",
"Name" : "John Doe",
"Appointment Made" : "4",
"Prospects Met" : "3",
"Policy Servicing" : "2"
}
}
}
you can get all values as Array of Dictionary and Sort it Like :
guard let Activities = jsonResult?["Activities"] as? [String:AnyObject] else {
return
}
var values = [Dictionary<String, AnyObject>]()
for (_, value) in Activities {
values.append(value as! Dictionary<String, AnyObject>)
}
let sorted = values.sorted { (dic1, dic2) -> Bool in
Int(dic1["Appointment Made"] as? String ?? "") ?? 0 > Int(dic2["Appointment Made"] as? String ?? "") ?? 0
}
print(sorted)
// model your data
for item in sorted {
let model = Model.init(jsonData: item)
// use model
}
// your model
class Model: NSObject {
var caseClosed :Int?
var name :String?
var appointmentMade :Int?
var prospectsMet :Int?
var policyServicing :Int?
override init() {
super.init()
}
init(jsonData : [String:AnyObject]) {
// map data to object
}
}
Use JsonSerialisation jsonObjectWithData to convert jSon response to NSArray object. And run a for loop, access every element in array as NSDictionary and compare their values for highest appointment.
To know more about accessing values from NSDictionary, apple docs reference
To know more about working with json in swift, apple docs reference.
I have tried this sorting:
let activitiesDict = jsonData?["Activities"] as? [String:Any]
let activitiesArray = activitiesDict.map({ [$0.0 : $0.1] })
let sortedActivitiesArray = self.activitiesArray.sorted {
(Int((($0 as! Dictionary<String, Any>)["Appointment Made"] as? String)!))! > (Int((($1 as! Dictionary<String, Any>)["Appointment Made"] as? String)!))!
}
print(sortedActivitiesArray)
Hope it helps.

Remove duplicates from array of dictionaries, Swift 3

Problem
I have an array of dictionaries as follows:
var arrayOfDicts = [
["Id":"01", "Name":"Alice", "Age":"15"]
["Id":"02", "Name":"Bob", "Age":"53"]
["Id":"03", "Name":"Cathy", "Age":"12"]
["Id":"04", "Name":"Bob", "Age":"83"]
["Id":"05", "Name":"Denise", "Age":"88"]
["Id":"06", "Name":"Alice", "Age":"44"]
]
I need to remove all dictionaries where there is a duplicate name. For instance, I need an output of:
var arrayOfDicts = [
["Id":"01", "Name":"Alice", "Age":"15"]
["Id":"02", "Name":"Bob", "Age":"53"]
["Id":"03", "Name":"Cathy", "Age":"12"]
["Id":"05", "Name":"Denise", "Age":"88"]
]
Order does not need to be preserved.
Attempted Solution
for i in 0..<arrayOfDicts.count
{
let name1:String = arrayOfDicts[i]["Name"]
for j in 0..<arrayOfDicts.count
{
let name2:String = arrayOfDicts[j]["Name"]
if (i != j) && (name1 == name2)
{
arrayOfDicts.remove(j)
}
}
}
This crashes though, I believe since I am modifying the size of arrayOfDicts, so eventually it j is larger than the size of the array.
If someone could help me out, that would be much appreciated.
I definitely recommend having a new copy rather than modifying the initial array. I also create storage for names already used, so you should only need to loop once.
func noDuplicates(_ arrayOfDicts: [[String: String]]) -> [[String: String]] {
var noDuplicates = [[String: String]]()
var usedNames = [String]()
for dict in arrayOfDicts {
if let name = dict["name"], !usedNames.contains(name) {
noDuplicates.append(dict)
usedNames.append(name)
}
}
return noDuplicates
}
You can use a set to control which dictionaries to add to the resulting array. The approach it is very similar to the one used in these answer and this
let array: [[String : Any]] = [["Id":"01", "Name":"Alice", "Age":"15"],
["Id":"02", "Name":"Bob", "Age":"53"],
["Id":"03", "Name":"Cathy", "Age":"12"],
["Id":"04", "Name":"Bob", "Age":"83"],
["Id":"05", "Name":"Denise", "Age":"88"],
["Id":"06", "Name":"Alice", "Age":"44"]]
var set = Set<String>()
let arraySet: [[String: Any]] = array.compactMap {
guard let name = $0["Name"] as? String else { return nil }
return set.insert(name).inserted ? $0 : nil
}
arraySet // [["Name": "Alice", "Age": "15", "Id": "01"], ["Name": "Bob", "Age": "53", "Id": "02"], ["Name": "Cathy", "Age": "12", "Id": "03"], ["Name": "Denise", "Age": "88", "Id": "05"]]
Please check this answer:
var arrayOfDicts = [
["Id":"01", "Name":"Alice", "Age":"15"],
["Id":"02", "Name":"Bob", "Age":"53"],
["Id":"03", "Name":"Cathy", "Age":"12"],
["Id":"04", "Name":"Bob", "Age":"83"],
["Id":"05", "Name":"Denise", "Age":"88"],
["Id":"06", "Name":"Alice", "Age":"44"]
]
var answerArray = [[String:String]]()
for i in 0..<arrayOfDicts.count
{
let name1 = arrayOfDicts[i]["Name"]
if(i == 0){
answerArray.append(arrayOfDicts[i])
}else{
var doesExist = false
for j in 0..<answerArray.count
{
let name2:String = answerArray[j]["Name"]!
if name1 == name2 {
doesExist = true
}
}
if(!doesExist){
answerArray.append(arrayOfDicts[i])
}
}
}
Several good answers already, but it was a fun exercise, so here's my solution. I'm assuming you don't care which of the duplicate entries are kept (this will keep the last one of the dupes).
func noDuplicates(arrayOfDicts: [[String:String]]) -> [[String:String]]
{
var noDuplicates: [String:[String:String]] = [:]
for dict in arrayOfDicts
{
if let name = dict["name"]
{
noDuplicates[name] = dict
}
}
// Returns just the values of the dictionary
return Array(noDuplicates.values.map{ $0 })
}
let uniqueArray = Array(Set(yourArrayWithDuplicates))
That should do the trick.
If you want to use just the name for uniqueness then create these as structs.
You shouldn't be doing anything with dictionaries. Much easier to work with data that makes sense.
Try this:
var uniqueNames = [String: [String:String] ]()
for air in arrayOfDicts {
if (uniqueNames[arr["Name"]!] == nil) {
uniqueNames[arr["Name"]!] = arr
}
}
result = Array(uniqueNames.values)
If you don't mind using an additional list:
var uniqueArray = [[String: String]]()
for item in arrayOfDicts {
let exists = uniqueArray.contains{ element in
return element["Name"]! == item["Name"]!
}
if !exists {
uniqueArray.append(item)
}
}

How to access contents of a dictionary with String as key and Arrays of Any as values?

I'm new to Swift and to programming in general. I have declared following dictionary:
let fruits: [String:[[Any]]] = [
"Apple": [
["Gravenstein",122,100],
["Ginger Gold", 119, 130],
["Jazz", 150, 190]
],
"Grapes": [
["Cabernet Sauvignon",120,100],
["Merlot", 150, 130],
["Tempranillo", 130, 190]
]
]
I want to program a function now that based on 2 variables, say, var selectedFruit = "Apple" and var selectedVariety = "Ginger Gold". Based on these 2 variables, I should be able to get the first number (122) and add it to the second number (100). I've tried map and flatmap to no success. Any help ?
Firstly I would change the way the dictionary is structured slightly. Instead of having an array of arrays, change it to have an array of dictionaries. So something like the following:
let fruits: [String:[String: [Int]]] = [
"Apple": [
"Gravenstein": [122,100],
"Ginger Gold": [119, 130],
"Jazz": [150, 190]
],
"Grapes": [
"Cabernet Sauvignon": [120,100],
"Merlot": [150, 130],
"Tempranillo": [130, 190]
]
]
Once you have done that you can simply write a method like this:
func getTotalValue(fruitName: String, varietyName: String) -> Int? {
guard let varieties = fruits[fruitName] else { return nil }
guard let values = varieties[varietyName] else { return nil }
return values.reduce(0, +)
}
And use it like so:
getTotalValue(fruitName: "Apple", varietyName: "Gravenstein")
Something like this will get your there:
let selectedFruit = "Apple"
let selectedVariety = "Ginger Gold"
if let arrays = fruits[selectedFruit] {
for array in arrays {
if let first = array.first as? String, first == selectedVariety {
if let num1 = array[1] as? Int, let num2 = array[2] as? Int {
print(num1 + num2)
}
}
}
}
But you've almost certainly chosen the wrong structure for your data. If you find yourself using Any, there's a better solution.
The easiest thing to do would be to have nested dictionaries with your fruits and varieties being the keys.
let fruits: [String: [String: [Int]]] = [
"Apple": [
"Gravenstein": [122, 100],
"Ginger Gold": [119, 130],
"Jazz": [150, 190]
],
"Grapes": [
"Cabernet Sauvignon": [120, 100],
"Merlot": [150, 130],
"Tempranillo": [130, 190]
]
]
if let values = fruits[selectedFruit]?[selectedVariety] {
print(values[0] + values[1])
}
Here is the function to find your fruits. I have added comments as well, so you might can modify the container for easier search.
// it is enough to say your container is [String: Any]
let fruits: [String: Any] = [
"Apple": [
["Gravenstein",122,100],
["Ginger Gold", 119, 130],
["Jazz", 150, 190]
],
"Grapes": [
["Cabernet Sauvignon",120,100],
["Merlot", 150, 130],
["Tempranillo", 130, 190]
]
]
func findFruits(name: String, type: String) -> (Int, Int)? {
// Declare nil tuple
var searchedFruitValues: (Int, Int)?
// First lets see if we have the fruit in the container
if let fruit = fruits[name] as? [[Any]]{
// Iterate through every element
fruit.forEach({ (element) in
// If the first element is equal to the searched type, check the numbers
if type == (element.first as? String) {
// If they are integers, lets associate the value to searchedFruitValues
if let first = element[1] as? Int, let second = element[2] as? Int {
searchedFruitValues = (first, second)
}
}
})
}
// Return nil or the found values
return searchedFruitValues
}
let searched = findFruits(name: "Apple", type: "Jazz")
print(searched ?? "not found")
Another solution:
struct Fruit {
var name : String
var variety : [Variety]
}
struct Variety {
var name : String
var numbers : [Int]
func getNumbers() -> Int {
var result = 0
for value in self.numbers {
result += value
}
return result
}
}
//example data
let apples = [
Variety(name: "Gravenstein", numbers: [122, 100]),
Variety(name: "Ginger Gold", numbers: [119, 130])
]
let grapes = [
Variety(name: "Cabernet Sauvignon", numbers: [120,100]),
Variety(name: "Merlot", numbers: [150, 130])
]
let fruits = [
Fruit(name: "Apple", variety: apples),
Fruit(name: "Garpes", variety: grapes)
]
//the actual call for the Variety numbers
if let goldenGinger = fruits.first(where: { $0.name == "Apple"})?.variety.first(where: { $0.name == "Ginger Gold"}) {
print(goldenGinger.getNumbers())
}

Resources