Search a dictionary in a custom object inside an array - arrays

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
}
}
}

Related

Changing static var embedded in an array?

I have a series of videos for the user to choose from. At any point, I want to be able to access the video property by calling Video.setupFull.watched and find out if it's nil or not. I created a struct and then static variables to create each video.
Here is my setup code:
struct Video {
let name: String
let image: String
let firebaseID: String
let number: Int
let url: String
let desc: String
var playbackTime: TimeInterval?
var watched: Bool?
var watchedDate: TimeInterval?
static let setupFull = Video(name: "Setup Full",
image: "Setup Full",
firebaseID: "setupTutorialFull",
number: 300928943,
url: "https://player.vimeo.com/external/282034045",
desc: "Moneypants Setup Tutorial Full")
static let step1Income = Video(name: "Step 1 - Income",
image: "Step 1 Income",
firebaseID: "step1Income",
number: 282034041,
url: "https://player.vimeo.com/external/282034041",
desc: "Moneypants Setup Tutorial (Step 1) - Income")
static let step2FamilyMembers = Video(name: "Step 2 - Family Members",
image: "Step 2 Family Members",
firebaseID: "step2FamilyMembers",
number: 282600740,
url: "https://player.vimeo.com/external/282600740",
desc: "Moneypants Setup Tutorial (Step 2) - Family Members")
static var array = [setupFull, step1Income, step2FamilyMembers]
}
To save space, I only store the optional parameters of the video objects (playbackTime, the watched, and the watchedDate) on Firebase, which looks like this:
{
"setupTutorialFull" : {
"playbackTime" : 194,
"watched" : true,
"watchedDate" : 1550004207
},
"step1Income" : {
"playbackTime" : 10,
"watched" : true,
"watchedDate" : 1544566787
},
"step2FamilyMembers" : {
"playbackTime" : 22,
"watched" : true,
"watchedDate" : 1544589461
}
}
(Note that all the videos in the list have been watched.)
I want to do a batch update when I initially load the data, so I made the array of videos and tried to update them like this:
static func observeDB(completion: #escaping () -> Void) {
FB.ref
.child(FB.videos)
.observe(.value) { (snapshot) in
for item in snapshot.children {
guard let snap = item as? DataSnapshot else { return }
guard let value = snap.value as? [String: Any] else { return }
let videoID = snap.key
let playbackTime = value[FB.playbackTime] as? TimeInterval
let watched = value[FB.watched] as? Bool
let watchedDate = value[FB.watchedDate] as? TimeInterval
// Find the appropriate video and update its variables.
if let index = array.firstIndex(where: { $0.firebaseID == videoID }) {
array[index].playbackTime = playbackTime
array[index].watched = watched
array[index].watchedDate = watchedDate
}
}
completion()
}
}
But, to my dismay, the static variables don't update when the array does.
print(Video.setupFull.watched) // prints `nil`
print(Video.array[0].watched) // prints `true`
Now if I manually modify the static var directly, it updates. This is expected, of course.
Video.setupFull.watched = true
print(Video.setupFull.watched) // prints `true`
The problems is I have potentially hundreds of videos to update, though, and don't want to have to manually update each one. That's why I created the static array. That way I could iterate over the array and update the videos quickly. I want to be able to call Video.setupFull.watched on a separate view controller and it to show watched.
My question: Why don't the static variables update when they are embedded in an array? Why can't I use Video.array[0].watched = true and it update the underlying Video.setupFull.watched object?
I thought that was how structs worked. They don't create copies, but are directly modifiable. What am I missing?

Swift - Check if a value belongs is in an array

I have an array of type "User" and I would like to check if a value belongs to a property type.
My code :
struct User: Identifiable {
var id = UUID()
var name: String
var age: String
}
var array: User = [
User[name: "AZE", age: "10"]
User[name: "QSD", age: "37"]
]
For example I'd like to know if "AZE" belongs to the property array "name". What is the function for retrieving this information. I hope you understood my problem and thank you for your answer.
First of all, arrays define with [Type] like [User]
Second of all init method calls as with (Arguments) like User(name: ,age:)
And last but not least, don't forget the ',' between elements of the array.
So
struct User: Identifiable {
var id = UUID()
var name: String
var age: String
}
var array: [User] = [
User(name: "AZE", age: "10"),
User(name: "QSD", age: "37")
]
So now you can check your element inside with contains like
array.contains(where: { user in user.name == "AZE" }) // returns `true` if it is
Tips
Try name arrays not array. Use plural names instead like users
To returtning the found one:
users.first(where: { user in user.name == "AZE" })
To summarizing it
users.first { $0.name == "AZE" }

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

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.

Trying to convert object mapper model to array

I'm new to Swift and have asked a few questions that I was somewhat confused about how to get the correct type for my object, but I believe I've figured it out.
I have a User object that is an Object Mapper model.
I am trying to convert this Object Mapper model to an Array.
https://github.com/Hearst-DD/ObjectMapper
However I am getting the error Cannot specialize a non-generic definition
Here is the variable I am trying to cast:
fileprivate var userDataSource = Mapper<User>().Array<Any>(User)
And an extension for its definition:
extension AccountViewController: GetUserDelegate {
func getUserSuccess(user: User) {
self.userDataSource = User
}
}
The original mapping to a model is done here:
guard let user = Mapper<User>().map(JSONObject: value)
The user class looks like this:
class User: Mappable {
required init?(map: Map) {
}
init() {}
var id: Int?
var firstName: String?
var lastName: String?
var displayName: String?
var image: URL?
var about: String?
var email: String?
var password: String?
var authToken: String?
I can include more if desired.
What am I misunderstanding here? None of the examples I'm seeing for this error really seem to apply to my situation.
Desired ultimate output:
["Name", "Bob Jim"],
["MC #", "1234567"],
["Company Name", "Bob's Truckin"],
["Truck Type", "Flat Bed"],
["Cell", "(555) 555-5555"],
["Dispatch", "(999) 999-9999"],
["Favorite Destinations", "Los Angeles"]
I gather you want an array of Users as the datasource,
I'd declare it like any other array:
var userDataSource = [User]()
or
var userDataSource = Array<User>()
And to update the datasource,
extension AccountViewController: GetUserDelegate {
func getUserSuccess(user: User) {
self.userDataSource.append(user)
}
}

Array is nil after appending to it

I have 2 Types, first is Car with property feature and it of the second type Feature.
I have a property cars: [Car], when I append new car to it, it returns nil.
I have created a sample in the snippet below:
class Car {
let make: String
var features: Feature?
let id: String
init(make: String, features: Feature?, id: String) {
self.make = make
self.features = features
self.id = id
}
}
class Feature {
let convertible: String
init(convertible: String) {
self.convertible = convertible
}
}
var cars: [Car]?
var feature: Feature?
let featureElement = Feature(convertible: "yes")
feature = featureElement
let car = Car(make: "SomeMaked", features: feature, id: "ID")
cars?.append(car)
print(cars)
My question is: Shouldn't the array increase its count after appening to it? What am I missing?
Please note that this code is just sample, so ignore typos and coding convention.
You haven't initialized your array.
Replace your var cars: [Car]? with var cars: [Car]? = []

Resources