I need to compare an item in one array of dictionaries to all the items in another array of dictionaries and find out if the first item was in the second array at all.
For example, array1 has a dictionary with [{"name": "zach", "age": "twenty"}, {"name": "ben", "age": "fifteen"}, etc.] in it. Array2 is similar with [{"name" = "eli", "age": "fifteen"}, {"name" = "will", "age": "fifteen"}, etc.].
If zach isn't in array2, I want to add him to it. How can I take the items from the first array of dictionaries and compare them to all the items in the second array so that I'll know whether or not I should add it?
Dictionary doesn't have duplicate key. In your ex you are having name key twice with in the same Dictionary and it is not possible, but if you have array something like below.
let array1 = [["name": "zach"], ["name": "eli"], ["name": "ben"]]
var array2 = [["name": "eli"], ["name" : "will"]]
Then you can try something like to add zach and ben name in array2.
let array2Name = array2.flatMap({$0["name"]}) // ["eli", "will"]
array2 = array1.reduce(array2) { !array2Name.contains($1["name"] ?? "") ? $0 + [$1] : $0 }
print(array2)
Output
[["name": "eli"], ["name": "will"], ["name": "zach"], ["name": "ben"]]
Edit: You are facing this issue because you are having array with type [[String:Any]] and in question you are showing as array of type [[String:String]], so change above code like this will works for you.
let array2Name = array2.flatMap({$0["name"] as? String}) // ["eli", "will"]
array2 = array1.reduce(array2) { !array2Name.contains($1["name"] as? String ?? "") ? $0 + [$1] : $0 }
print(array2)
I'm guessing that your question may be more complicated, but Dictionary is Equatable based on its contents, which means that you can iterate over the first Array, and query the second Array, as to whether it contains the same Dictionary. Like this:
let array1 : [Dictionary<String, String>] = [["one" : "two"], ["three" : "four"]]
let array2 : [Dictionary<String, String>] = [["one" : "two"], ["three" : "five"]]
var doesContain = false
for dict in array1 {
if array2.contains(where: { $0 == dict} ) {
doesContain = true
}
}
print(doesContain) // Should be true in this case, because both arrays contain ["one" : "two"]; if they had no duplicates, doesContain would remain false
If I understood correctly, you want to have a new array of unique dictionaries. This is what I can suggest you:
let array1: [String] = [["name":"test1"], ["name":"test12"]].flatMap { $0["name"] }
let array2: [String] = [["name":"test1"], ["name":"test3"]].flatMap { $0["name"] }
let set = Set<String>(array1).union(Set<String>(array2))
let resultArrayOfDicts = set.map{ ["name": $0] }
Related
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"]]
I tried to append an array of dictionaries which are coming from server to a globally declared array.but i am getting an error like "Cannot convert value of type '[Any]' to expected argument type '[String : Any]'"if anyone helps me would be great.Thanks in advance
var pro = [[String:Any]]()
var productsdetails = Array<Any>()
productsdetails = userdata.value(forKey: "products") as! Array
print("response\(productsdetails)")
self.pro = self.pro.append(productsdetails)
print(self.pro)
Use this code like below, i hope this works
var pro = [[String:Any]]()
if let productsdetails = userdata.value(forKey: "products") as? [[String:Any]] {
print("response\(productsdetails)")
self.pro.append(productsdetails)
print(self.pro)
}
to solve this iterate
var pro = [[String:Any]]()
if let productsdetails = userdata.value(forKey: "products") as? [[String: Any]] {
for details in productsdetails {
pro.append(details)
}
}
or you may use directly self.pro = productsdetails if you not to want iterate
**image shows [String : String] as I have used [String : String] instead of [String : Any]*
You can try this: (Swift-4.2)
var data: [String: Any] = [
"key1": "example value 1",
"key2": "example value 2",
"items": []
]
for index in 1...3 {
let item: [String: Any] = [
"key": "new value"
]
// get existing items, or create new array if doesn't exist
var existingItems = data["items"] as? [[String: Any]] ?? [[String: Any]]()
// append the item
existingItems.append(item)
// replace back into `data`
data["items"] = existingItems
}
Answer same as: Append to array in [String: Any] dictionary structure
Can you show us your response which has key products? As there is mismatch with the variable pro and productdetails. As i see pro holds values as "Array of Dictionary with key as String type and Value as Any type" which again has Array above it and productdetails is expecting Array of Any type not the Array of Dictionary Type. Assuming your products has Array of String Type or can be even a class object type you can do it as below.
var pro = Array<Any>() //Or You can make Array<String>() or Array<CustomClass>()
var userdata:[String:[String]] = ["products":["One","Two","Three"]]
var productsdetails = Array<Any>()
productsdetails = userdata["products"] ?? []
print("response\(productsdetails)")
pro.append(productsdetails)
I have created an Array of Dictionaries:
let tempArray = [["id":"1","Name":"ABC"],["id":"2","Name":"qwe"],["id":"3","Name":"rty"],["id":"4","Name":"uio"]]
Now I have to create an array of Name only.
What I have done is this:
var nameArray = [String]()
for dataDict in tempArray {
nameArray.append(dataDict["Name"]!)
}
But is there any other efficient way of doing this.
You can use flatMap (not map) for this, because flatMap can filter out nil values (case when dict doesn't have value for key "Name"), i.e. the names array will be defined as [String] instead of [String?]:
let tempArray = [["id":"1","Name":"ABC"],["id":"2","Name":"qwe"],["id":"3","Name":"rty"],["id":"4","Name":"uio"]]
let names = tempArray.flatMap({ $0["Name"] })
print(names) // ["ABC", "qwe", "rty", "uio"]
Use compactMap as flatMap is deprecated.
let tempArray = [["id":"1","Name":"ABC"],["id":"2","Name":"qwe"],["id":"3","Name":"rty"],["id":"4","Name":"uio"]]
let name = tempArray.compactMap({ $0["Name"]})
print(name)
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)
}
}
I have a multidimensional dictionary and I am trying to add data into it without deleting data, but overwrite if duplicate.
var items = [Int: AnyObject]()
var IdsAndDetails = [String:AnyObject]()
let index = 3 // static for test
...
for result in results {
// result is String (result also indicates itemId)
let details = self.IdsAndDetails[result]
// details is AnyObject like [String:String]
if let itemDetails = details {
// Here I want to append data into 'items' variable
// This doesn't work: (Error-1)
self.items[index]![result] = itemDetails
}
(Error-1):
Cannot assign to immutable expression to type AnyObject.
However, if I try like, it works but it's not the approach I want. It's re-creating the dictionary. Instead, I want to append the data.
self.items = [
index : [result : itemDetails]
]
The structure of dictionary I want to get in the end is:
items = [
index : [
"id1" : ["key": "value", "key": "value"],
"id2" : ["key": "val", "key": "val"],
],
index : [
"id3" : ["key": "val", "key": "val"],
"id4" : ["key": "val", "key": "val"],
"id5" : ["key": "val", "key": "val"],
]
]
// index is Integer
// [Key:Value] is [String:String] - itemDetails equal to all `[key:value]`s
// 'id' is also String
Update: I also tried, but no luck
let a = self.items[index]
a![result]! = itemDetails as [String:String]
Update 2:
let valueDict = (value as! NSDictionary) as Dictionary
for (key, val) in valueDict {
let keyString = key as! String
let valString = val as! String
self.items[index]![result]![keyString]! = valString
}
But it's throwing error:
fatal error: unexpectedly found nil while unwrapping an Optional value
But Surprisingly debugging shows all values:
po index : 1
po itemId : "123123"
po keyString: "keyInString"
po valString: "valInString"
Update 3:
for index in 1...5 {
var results = [String]()
// First I retrieve nearby users and assign it to key
let itemsRef = Firebase(url: self.secret + "/items")
eventsRef.queryOrderedByChild("user_id").queryEqualToValue(key).observeEventType(.ChildAdded, withBlock: { snapshot in
// ^ above 'key' is the user_id retrieved before
let itemDetails = snapshot.value // item details - [key:val, key:val]
let itemId = snapshot.key // item ids [id1,id2,id3]
// I used 'KeyAndDetails' to store all values with ids
let IdsAndDetails = [itemId: itemDetails]
self.itemIdsArray = []
self.itemIdsArray.append(itemId)
if index == 1 {
// self.items = [
// index : [itemId : itemDetails]
// ]
// ^ This worked and gave me the structure
// but I don't want to overwrite it, instead, I want to append
// on the dictionary
// This is where I am trying to append into `self.items`,
// and throws error:
self.items[index]?[result] = (eventDetails as! [String : String])
}
...
It seems like you're trying to bypass Swift's type system instead of working with it. Instead of using AnyObject, you should be trying to use the exact type you want for the value of that dictionary. In this case it looks like you want something like [Int: [String: [String: String]]] (although, like #EricD said in the comments, you should probably be using a struct instead, if at all possible).
Here's a quick (static) example similar to the code in your question:
var items = [Int: [String: [String: String]]]()
let idsAndDetails = ["id2": ["key3": "value3"]]
let index = 3
items[index] = ["id1": ["key1": "value1", "key2": "value2"]]
let result = "id2"
if let itemDetails = idsAndDetails[result] {
items[index]?[result] = itemDetails
}
At the end of that, items will be:
[3: ["id1": ["key1": "value1", "key2": "value2"], "id2": ["key3": "value3"]]]
The ? in items[index]?[result] tells Swift to make sure items[index] is non-nil before attempting to execute the subscript method. That way, if you try to update an index that doesn't exist in items you don't cause a crash.