How to group Array of Dictionaries by a key in swift? - arrays

For example, I have this array of dictionaries
[["Country":"Egypt","Name":"Mustafa","Age":"20"],["Country":"Palestine","Name":"Omar","Age":"15"],["Country":"Egypt","Name":"Ali","Age":"40"],["Country":"Jordan","Name":"Ahmad","Age":"25"],["Country":"Palestine","Name":"Amani","Age":"30"],["Country":"Jordan","Name":"Mustafa","Age":"20"]]
I want to group them by Country to become
{"Egypt": [{"Country":"Egypt","Name":"Mustafa","Age":"20"} {"Country":"Egypt","Name":"Ali","Age":"40"}],
"Palestine": [{"Country":"Palestine","Name":"Amani","Age":"30"},{"Country":"Palestine","Name":"Omar","Age":"15"}],
"Jordan":[{"Country":"Jordan","Name":"Ahmad","Age":"25"},{"Country":"Jordan","Name":"Mustafa","Age":"20"}]
}
Please help.

Swift has a nice function that does this for you...
let people = [["Country":"Egypt","Name":"Mustafa","Age":"20"],["Country":"Palestine","Name":"Omar","Age":"15"],["Country":"Egypt","Name":"Ali","Age":"40"],["Country":"Jordan","Name":"Ahmad","Age":"25"],["Country":"Palestine","Name":"Amani","Age":"30"],["Country":"Jordan","Name":"Mustafa","Age":"20"]]
let peopleByCountry = Dictionary(grouping: people, by: { $0["Country"]! } )
peopleByCountry will now be the format that you want.
You can read more about this function in the documentation.
Just to add to Hamish's comment.
You really shouldn't be working with Dictionaries here. You should be working with Structs...
struct Person {
let countryName: String
let name: String
let age: Int
}
Even better would be to have a Country struct...
struct Country {
let name: String
}
and use that in the Person for their country property instead of String.

let arrCountry: [[String:String]] = [["Country":"Egypt","Name":"Mustafa","Age":"20"],
["Country":"Palestine","Name":"Omar","Age":"15"],
["Country":"Egypt","Name":"Ali","Age":"40"],
["Country":"Jordan","Name":"Ahmad","Age":"25"],
["Country":"Palestine","Name":"Amani","Age":"30"],
["Country":"Jordan","Name":"Mustafa","Age":"20"]]
func sortCountry() {
var sortedCountries : [String : [[String:String]]] = [:]
for object in arrCountry {
let country = object["Country"] as! String
if var arrCountry = sortedCountries[country] {
arrCountry.append(object)
sortedCountries[country] = arrCountry
}
else {
sortedCountries[country] = [object]
}
}
}

Well I would go like this:
Get all the countries by traversing the array once and store it in an array.
Loop for this array of countries.
Filter the array with predicate where country is current country.
Store this in the final dictionary of country.

Related

Search a dictionary in a custom object inside an array

I’m building search functionality. I have an array of User objects and each User has a dictionary of tags. I’m trying to return users with searched tags.
My user class is:
class User: NSObject {
var name: String?
var tags = Dictionary<String, String>()
}
An example of the tags is:
tags: {
“entry1” : “test1”,
“entry2” : “test2”,
“entry3” : “test3”
}
I’ve been trying variances of:
let predicate = NSPredicate(format: “tags contains[c] %#", “test1”);
let filteredArray = self.usersArray!.filter { predicate.evaluate(with: $0) }; print(“users = ,\(filteredArray)");
It’s throwing “this class is not key value coding-compliant for the key tags.’ I'm not sure how to search inside the User object.
Update:
I've change the dictionary to an array and
filter { $0.tags.contains(searchTerm) } works for exact search match, as suggested.
.filter( {$0.tags.reduce(false, {$1.contains(searchTerm)} )} ) does not work for a partial match however, which is what i'm looking for.
Here is what is printed:
Search term: ale
Result: []
Tags array inside each object:
["Alex", "Will", ""]
["Bob", "Dan", ""]
["First", "Second", "Third"]
["Testing 1", "Testing 2", ""]
Your implementation of tags seems cumbersome. Here is a simpler one.
class User {
var name: String
var tags: [String]
}
Now let's say you have an array of users in users.
var users: [User]
// Store users in the array
Now, this is how you check which user contains a particular tag.
let tag = "yourTag"
users.filter( { $0.tags.contains(tag) } )
However, if you are adamant that you need the tags in a dictionary, then you could do it like this,
users.filter( {$0.tags.values.contains(tag)} )
If you want to check if the tag is part of any of the tags in a particular user, you could do it like this,
let filteredUsers = users.filter( {$0.tags.reduce(false, {$1.contains(tag)} )} )
P.S - we don't use ; in Swift.
It's good practice to make your models conform to Codeable:
struct User: Codable {
var userId: UUID
var name: String = ""
var referenceTags: [String: String] = [:]
}
// Create some users:
let Bob = User(userId: UUID(), name: "Bob", referenceTags: ["tag1": "no", "tag2": "yes"])
let Steve = User(userId: UUID(), name: "Steve", referenceTags: ["tag2": "no", "tag3": "no"])
Create an array of users to filter.
let users:[User] = [Bob, Steve]
Get a search predicate or string from your Search bar or other.
let searchPredicate = "yes"
Filter and iterate over tags for the required value.
let results = users.filter { (user) -> Bool in
for (key, _) in user.referenceTags.enumerated() {
if (user.referenceTags[key] == searchPredicate) {
return true
}
}
}

How to print elements of one class using variable of another class?

I have created two classes a Person class and a food class. Now i am making list of all the food items consumed in a year by a particular person. I want to print all of them separated by commas
Here's my code
class Food {
let name: String
var EatenBy: Person?
init(name: String){
self.name = name
}
}
And my Person class is as follows
class Person {
var name: String
var foods: [Food] = []
lazy var foodNames: () -> String = {
return ""
}
init(name: String){
self.name = name
}
func adopt(food: Food){
foods.append(food)
food.EatenBy = self
}
}
Now i want to create different food items using the Food class and then assign it to a person who have consumed them and store it in an array foods.
var person = Person(name: "Test")
var pasta = Food(name: "pasta")
Can anyone help me out how can i use the objects created using Food class and assign it to a object created in the Person class and append them in the foods array ?
My final aim is to print all the elements in the foods array separating them using commas or spaces or such, which i guess can be easily done by looping each of the element through a for loop ?
Using a Person instance, you can access its properties (including the foods array by the dot syntax: person.foods.
For printing the food names, you should declare foodNames as a computed property. As its return value, just use map to get the names of each Food instance, then use [String].joined(separator: ",") to join the contents of the array of Strings into a single String.
class Person {
var name: String
var foods: [Food] = []
var foodNames:String {
return foods.map{$0.name}.joined(separator: ",")
}
init(name: String){
self.name = name
}
func adopt(food: Food){
foods.append(food)
food.EatenBy = self
}
}
class Food {
let name: String
var EatenBy: Person?
init(name: String){
self.name = name
}
}
let john = Person(name: "John")
let pasta = Food(name: "pasta")
john.foods = [pasta, Food(name: "tuna")] //assign an array to the variable directly
john.foods.append(Food(name: "tomato")) //or append the existing array
john.adopt(food: Food(name: "bread")) //you can also use your _adopt_ function to add an element to the _foods_ array of a _Person_ instance
john.foodNames // "pasta, tuna, tomato,bread"
It looks like you are missing some basic concepts of how Swift works (like using the dot syntax to access class properties), so I would recommend reading The Swift Programming Language Guide as a start, it is a really useful starting point for a beginner Swift developer.

deleting objects if string of object matches string in a separate array

I have an nsobject class with four strings
class Post: NSObject {
var author: String!
var postID: String!
var pathToImage: String!
var userID: String!
}
I also have a separate class viewcontroller which has a function grabbing posts from firebase. I have an array called posts = [Post](), which is filled by a seperate function going through firebase and grabbing data for each photo. I also have an array called removeArray which is array of strings, which the string is the postID of certain posts. Now this is my problem, I am trying to loop through removeArray, check if the each in removeArray = to the each in posts.postID and check if they are equal. Then either I delete that each in posts.postID post, or I create a new array that is posts - posts with postID's in removeArray. Here is my code now that does not work, it just keeps posts as is.
if posts != nil {
if var array = UserDefaults.standard.object(forKey: "removeArray") as? [String] {
for each in posts {
for one in array {
if one == each.postID {
new.append(each)
}
}
}
return self.posts.count
}
}
So if you have any idea how to take a string in an array, check if that string if eqaul to a string in an array of objects.postID, and remove that object from the array if it is equal. I have tried to research a way to filter it, but so far nothing. Please give me some feedback. Thanks
My problem = http://imgur.com/a/m5CiY
var posts = [p1,p2,p3,p4,p5]
let array = ["aaa","bbb"]
var new:Array<Post> = []
for each in posts {
for one in array {
if one == each.postID {
new.append(each)
}
}
}
print("This objects should be remvoed: \(new)")
posts = Array(Set(posts).subtracting(new))
print("After removing matching objects: \(posts)")
You could use reduce(_:_:)!
class Country {
var name: String!
init(name: String) {
self.name = name
}
}
let countries = [Country(name: "Norway"), Country(name: "Sweden"), Country(name: "Denmark"), Country(name: "Finland"), Country(name: "Iceland")]
let scandinavianCountries = ["Norway", "Sweden", "Denmark"]
// Store the objects you are removing here
var nonScandinavianCountries: [Country]?
let scandinavia = countries.reduce([Country](), {
result, country in
// Assign result to a temporary variable since result is immutable
var temp = result
// This if condition works as a filter between the countries array and the result of the reduce function.
if scandinavianCountries.contains(country.name) {
temp.append(country)
} else {
if nonScandinavianCountries == nil {
// We've reached a point where we need to allocate memory for the nonScandinavianContries array. Instantiate it before we append to it!
nonScandinavianCountries = []
}
nonScandinavianCountries!.append(country)
}
return temp
})
scandinavia.count // 3
nonScandinavianCountries?.count // 2
Resouces:
https://developer.apple.com/reference/swift/array/2298686-reduce

How do I set a value of a variable to an array within a dictionaries key value?

I have stored a users home address in firebase as an array. It looks like this:
I am now trying to grab all of my users data and store them into a dictionary like this:
The User() part of the code is defined above like this:
var users = [User]()
the [User]() part of it comes from a user.swift file which is here:
class User: NSObject {
var fullName: String?
var email: String!
var userPhoto: String?
var homeAddress: [String:[Double]]()
var schoolOrWorkAddress: String!
}
the error is with the homeAddress part of it.
In the file I create variables for all my user data storage. My question now is how do I set home address to a value type of an array as that dictionaries key value. Can anyone help? Feel free to comment if you have any questions in helping me.
You could try to override the setValue function to iterate through homeAddress points.
// value in this case would be the points in your dictionary and the
// the key would be "homeAddress"
override func setValue(value: AnyObject?, forKey key: String) {
if key == "homeAddress" {
let homeAddressPoints = [HomeAddressPoint]()
for dict in value as! [[String: AnyObject]] {
let point = HomeAddressPoint()
point.setValuesForKeysWithDictionary(dict)
homeAddressPoints?.append(point)
}
} else {
super.setValue(value, forKey: key)
}
}
This is just an idea, and hopefully you can build from this.

Obtain a subset of tuple array in swift

I have a array of tuples like the following
var customProducts = [(productType: String, info:[String:AnyObject?])]
The parameter "productType" works like a "product category", like fruits, grains, beverage, etc.
The parameter "info" is a dictionary of nutritional information of the product.
I want to get a subset of the tuples array, based on the productType so I could obtain just the "info" dictionary for an specific productType. In C# I would try something like the following using Linq:
var fruits = customProducts.Where(q=>q.productType == "fruit").Select(q => q.info) as List<KeyValuePair<string, object>>;
How may I archive the same results using Swift (2.x)?
Thanks!
I think the Swift equivalent would be:
let fruits = customProducts.filter { $0.productType == "fruit" }.map { $0.info }
Here fruits is [[String : AnyObject?]], an array of dictionaries (an array of info, the same as your List<KeyValuePair<string, object>> if I'm not mistaken).
You can use the filter method
let beverageInfo = (customProducts.filter { $0.productType == "Beverage" }).first?.info
Now beverageInfo is [String : AnyObject?]?, an optional dictionary representing the info for "Beverage" tuple.
You should filter the array according to what type the product is
Eg
var customProducts = [(productType: String, info:[String:AnyObject?])]
let fruitProducts = customProducts.filter { product in
if product.productType == "fruit" {
return true
} else {
return false
}
}.map { $0.info }
Then you can use fruitProducts however you want.

Resources