I am trying to perform search operation in swift - swift

I have an array of managed object class named as categories
In every category, I have NSSet of apCodes
I am showing the category name in the table view section and opCodes name in table view rows.enter image description here
I want to search operation by apcodes, not by category name, but I want to show only those categories whose apcode name matches with the search text.
Here is my code, please correct my function, I unable to filter apcodes properly.
Note: I have copied the original categories array in originalCategories
func filterArray(text : String) {
categories = originalCategories
categories = categories?.compactMap { category in
guard !text.isEmpty else {
return category
}
var apCodes = [ApCode]()
if let apc = category.apCodes?.allObjects as? [ApCode] {
apCodes = apc.filter { (apcode) -> Bool in
if apcode.apCodeName?.lowercased().contains(text.lowercased()) ?? false {
return true
} else {
return false
}
}
}
if apCodes.count == 0 {
return nil
} else {
category.apCodes = NSSet(array: apCodes)
return category
}
}
if text == Constant.EMPTY_STRING {
categories = originalCategories
}
}

Related

Combine array of object from different endpoint so it can also pagination

I have 2 different endpoint:
The first one have a pagination.
The second one doesn't have pagination
I mapping the object from the first and second endpoint so they have the same object when i display it and limit only 10 item.
The Question is..
Is that possible to combine the API called so i can use pagination with different endpoint? so the result is
Merge the object into 1
Sort by date
Limit the item only 10 item
So far i can't figure it out how to combine an API, this is my service setup
func getMessageList(page: Int) -> Single<[Message]> {
return platformBanking.getMessageList(token: sessionStore.token, page: page, pageSize: 10)
}
func getMoInbox() -> Single<[Message]> {
return Single.create { single in
MOInbox.sharedInstance.getInboxMessages { inboxMessages, accountMeta in
var messages: [Message] = []
inboxMessages.forEach { entry in
let message: Message = .init()
message.title = entry.notificationTitle
message.subtitle = entry.notificationSubTitle
message.body = entry.notificationBody
message.messageType = !(entry.campaignID?.isEmpty ?? false) ? 5 : 1
message.imageName = entry.notificationMediaURL ?? ""
message.date = entry.receivedDate?.string(withFormat: "dd MMM") ?? ""
message.isRead = entry.isRead
message.action = entry.deepLinkURL ?? ""
messages.append(message)
}
single(.success(messages))
}
return Disposables.create()
}
}
This is in my ViewModel
var filterMessages: [Message] = []
private var page: Int = 1
private var isLoading: Bool = false
private var endOfMessage: Bool = false
private func getMessageInboxList() {
var inboxMessages: [Message] = []
isLoading = true
Single.zip(manageMessages.getMessageList(page: page), manageMessages.getMoInbox())
.subscribe(onSuccess: { [weak self] firstMessage, secondMessage in
inboxMessages.append(contentsOf: firstMessage)
inboxMessages.append(contentsOf: secondMessage)
self?.processMessages(messages: inboxMessages)
}).disposed(by: disposedBag)
}
private func processMessages(messages: [Message]) {
self.messages.append(contentsOf: messages)
self.filterMessages = self.messages.sorted(by: { $0.date > $1.date })
eventShowHideLoadingIndicator.onNext(false)
if messages.count < 10 {
endOfMessage = true
}
eventMessagesDataUpdated.onNext(())
isLoading = false
}
This is a function to called pagination in viewModel, when i try paginate i just realize i make a duplicate item from getMoInbox API called. but still combining the object and limiting by 10 item i still can't find the answer.
func loadMoreMessageInbox() {
guard !endOfMessage, !isLoading, selectedIndex == 0 else { return }
page = page + 1
getMessageInboxList()
}
Please help me guys.
This requires a state machine. There are a number of different libraries that you could use (a partial list is at the bottom.)
Here is an example using the cycle function from my library.
enum Input {
case nextPageRequested // emit this to `input` when you are ready for the next page.
case pageReceived(Int, [Message]) // this is emitted with the page results.
}
struct State<T> {
var pages: [Int: [T]] = [:] // stores the pages as they come in. The MoMessages will be stored in page 0
}
func example(input: Observable<Input>, messageManager: MessageManager) -> Observable<[Message]> {
Single.zip(messageManager.getMoInbox(), messageManager.getMessageList(page: 1))
.asObservable()
.flatMap { moMessages, page1Messages in
// create state machine initialized with the moMessages and page1Messages
cycle(
input: input,
initialState: State(pages: [0: moMessages, 1: page1Messages]),
reduce: { state, input in
// when a new page is received, store it
if case let .pageReceived(page, messages) = input {
state.pages[page] = messages
}
},
reaction: reaction(
request: { state, input in
// when a new page is requested, figure out what page number you need and return it (otherwise return nil)
guard case .nextPageRequested = input else { return nil }
return state.pages.keys.max() + 1
},
effect: { page in
// get the page you need
messageManager.getMessageList(page: page)
.map { Input.pageReceived(page, $0) }
.asObservable()
}
)
)
}
.map { state in
// sort the messages in the pages and return them
state.pages.values.flatMap { $0 }.sorted(by: { $0.date > $1.date })
}
}
Here's that promised list:
My CLE library contains a state machine system.
RxFeedback is the OG tool, developed by the initial designer of RxSwift.
RxState is part of the RxSwiftCommunity.

Firestore query issues

I am having an issue where I run this function:
static func getAllJokes(reset: Bool, completion: #escaping () -> Void) {
Utilities.getDadJokes(reset: reset) {
print("Dad Jokes Pass, array = \(DadJokes.dadJokes)")
Utilities.getAssistantJokes(reset: reset) {
print("Assistant Jokes Pass, array = \(AssistantJokes.assistantJokes)")
Utilities.getKnockKnockJokes(reset: reset) {
print("Knock Knock Jokes Pass, array = \(KnockKnockJokes.knockKnockJokes)")
Utilities.getRandomJokes(reset: reset) {
print("Random Jokes Pass, array = \(RandomJokes.randomJokes)")
completion()
}
}
}
}
}
getDadJokes:
db.collection("jokes").document("Dad Jokes").addSnapshotListener { document, error in
//check for error
if error == nil {
//check if document exists
if document != nil && document!.exists {
if let JokeNum = document!.get("JokeNum") as? Int {
self.countDadJokes = JokeNum
UserDefaults.standard.setValue(JokeNum, forKey: "countDadJokes")
print("DadJokeNum = \(self.countDadJokes)")
}
// var DadJokes.dadJokes.count = 1
print("count = \(DadJokes.dadJokes.count)/\(self.countDadJokes)")
print("countDadJoke = \(self.countDadJokes)")
self.jokes.removeAll()
if reset == true {
DadJokes.dadJokes.removeAll()
}
// for _ in 0...self.countDadJokes {
while DadJokes.dadJokes.count <= self.countDadJokes {
// print("count = \(DadJokes.dadJokes.count)/\(self.countDadJokes)")
if let Joke = document!.get("\(DadJokes.dadJokes.count + 1)") as? String {
print("DadJokeNum = \(self.countDadJokes)")
if Utilities.jokes.contains("\(Joke) - From Dad Jokes") {}else {
Utilities.jokes.append("\(Joke) - From Dad Jokes")
DadJokes.dadJokes.append(Joke)
UserDefaults.standard.set(DadJokes.dadJokes, forKey: defaults.dadJokes.rawValue)
Utilities.updateJokesDefaults()
print("countDadJokesSaved = \(DadJokes.dadJokes.count)")
print("DadJokesSaved = \(DadJokes.dadJokes)")
}
print("Dad Joke: \(Joke)")
//print("count = \(DadJokes.dadJokes.count)/\(self.countDadJokes)")
if DadJokes.dadJokes.count == self.countDadJokes {
completion()
}
}
}
}
}
}
runs fine, but then when I run getAssistantJoke (the same thing):
print("assistant get running")
db.collection("jokes").document("Assistant Jokes").addSnapshotListener { document, error in
//check for error
if error == nil {
//check if document exists
if document != nil && document!.exists {
if let JokeNum = document!.get("JokeNum") as? Int {
self.countAssistantJokes = JokeNum
UserDefaults.standard.setValue(JokeNum, forKey: defaults.countAssistantJokes.rawValue)
print("DadJokeNum = \(self.countDadJokes)")
}
// var DadJokes.dadJokes.count = 1
print("count = \(AssistantJokes.assistantJokes.count)/\(self.countAssistantJokes)")
print("countAssistantJokes = \(self.countAssistantJokes)")
if reset == true {
AssistantJokes.assistantJokes.removeAll()
}
// for _ in 0...self.countDadJokes {
while AssistantJokes.assistantJokes.count <= self.countAssistantJokes {
// print("count = \(AssistantJokes.assistantJokes.count)/\(self.countAssistantJokes)")
if let Joke = document!.get("\(DadJokes.dadJokes.count + 1)") as? String {
print("AssistantJokeNum = \(self.countAssistantJokes)")
if Utilities.jokes.contains("\(Joke) - From Assistant Jokes") {}else {
Utilities.jokes.append("\(Joke) - From Assistant Jokes")
AssistantJokes.assistantJokes.append(Joke)
UserDefaults.standard.set(AssistantJokes.assistantJokes, forKey: defaults.assistantJokes.rawValue)
Utilities.updateJokesDefaults()
print("countAssistantJokesSaved = \(AssistantJokes.assistantJokes.count)")
print("AssistantJokesSaved = \(AssistantJokes.assistantJokes)")
}
print("Assistant Joke: \(Joke)")
// print("count = \(AssistantJokes.assistantJokes.count)/\(self.countAssistantJokes)")
if AssistantJokes.assistantJokes.count == self.countAssistantJokes {
completion()
}
}
}
}
}
}
}
It stops at firebase query. The documents and everything exist, therefor, what's the issue? Everything exists and I am truly stumped. My goal is to retrieve a ton of data and display a random subject. I feel like this should be easier than it is.
Here is my firebase setup:
I feel like the main problem here is the way in which you are modelling the data in Firestore.
You have a collection called jokes but then a document called Assistant Jokes, Dad Jokes, etc...
I think a much better structure would be for each document to be a single joke inside the jokes collection. Use an "auto id" for each joke and then put a type inside the joke document.
The fact that you have to maintain a JokeNum next to your list of jokes is a sign that something is wrong.
By structuring this way you would then be able to get all jokes or filter the jokes by a particular type etc...
Your code is super complex at the moment but I think the best approach is to structure your data in a way that helps your code.

Question about using Arrays with the State property

Currently, I am using #State for an Array of SKProducts
#State var products = [SKProduct]()
var body: some View {
ScrollView(.horizontal){
LazyHStack {
if products.count > 0 {
ForEach(products.indices) { index in
let product = self.products[index]
// ProductCell(product: product)
}
ForEach(1...products.count, id: \.self) { count in
let product = $products[count]
// ProductCell(product: Binding.constant(products[count]))
// ProductCell(name: <#Binding<String>#>).padding()
}
}
}
}.onAppear(perform: {
Purchases.shared.offerings { (offerings, error) in
if let tempPackages = offerings?.current?.availablePackages {
for package in tempPackages {
products.append(package.product)
}
}
}
})
}
I am trying to pass off an individual product to another view below:
ForEach(1...products.count, id: \.self) { count in
let product = $products[count]
}
However "product" is considered an "error type"
I am new to SwiftUI and I cannot for the life of me figure out what I am doing wrong. Thank you for your time and consideration.
products is an array, $products is a binding.
products[0] is the first element of an array. $products[0] means nothing because a binding doesn't have a subscript accessor.
Remove the $.

How to group array of objects and transform into another array of grouped objects

Let's say I have array of Report Objects and GroupedReport Objects
This is my Report Class Structure
class Report {
var report_date:String
var problem:String
var description:String
var is_know:Int
var is_solved:Int
init(report_date:String,problem:String,description:String,is_know:Int,is_solved:Int) {
self.report_date = report_date
self.problem = problem
self.description = description
self.is_know = is_know
self.is_solved = is_solved
}
}
This is my GroupedReport Class structure
class GroupedReport{
var problem:String
var report_array:[Report]
init(problem:String){
self.problem = problem
report_array = []
}
public var description: String { return "GroupedReort:\nProblem:\(self
.problem)\nArray:\(self.report_array)" }
}
Is there any algorithm to group Report objects with the same problem (class variable of Report) value and transform into GroupedReport objects?
I have done the implementation of my own and my code is not working as I expected.Could someone help me please? Thanks for your attention
var reports:[Report] = []
var problem_array:[String] = []
var grouped_reports: [GroupedReport] = []
func group_array(){
for i in 0...self.reports.count-1{
print("\(i) Loop ::")
print("============")
var problem_found:Bool = false
// j loop start
if problem_array.count > 0 {
for j in 0...self.problem_array.count-1{
print("problem_array[j]: \(problem_array[j]) <compare to> reports[i].problem: \(reports[i].problem)")
print("")
if(problem_array[j] == reports[i].problem){
print("")
print("problem_array[j] \(problem_array[j]) <is equal to> reports[i].problem \(reports[i].problem)")
problem_found = true
// this is the existing problem
}
}
}
// j loop end
if(problem_found){
//find problem key and append array to that key
for x in 0...self.grouped_reports.count-1{
if self.grouped_reports[x].problem == reports[x].problem{
print("")
print("Problem found")
print("Append Report problem :\(reports[x].problem)")
print("Append Report des :\(reports[x].description)")
self.grouped_reports[x].report_array.append(reports[x])
}
}
}
else{
// create new problem key
problem_array.append(reports[i].problem)
//crete new group_report with new problem and append current report[i] to that report , append that group_report to the group_report array
var group_report = GroupedReport(problem: reports[i].problem)
group_report.report_array.append(reports[i])
print("")
print("new problem")
print("Append Report problem :\(reports[i].problem)")
print("Append Report des :\(reports[i].description)")
self.grouped_reports.append(group_report)
}
}
print("!!Final Array!!")
print("=================")
for i in 0...grouped_reports.count-1 {
print("\(i) Array!")
print("-----------")
print("Problem:\(self.grouped_reports[i].problem)")
print("Inner Array")
print("count: \(grouped_reports[i].report_array.count)")
for j in 0...grouped_reports[i].report_array.count-1{
print(grouped_reports[i].report_array[j].description)
}
}
}
Use code like this to generate the grouped reports:
var groupedReports: [String: GroupedReport] = [:]
reports.forEach { report in
if let groupedReport = groupedReports[report.problem] {
groupedReport.report_array.append(report)
} else {
let groupedReport = GroupedReport(problem: report.problem)
groupedReport.report_array.append(report)
groupedReports[report.problem] = groupedReport
}
}
Then you can loop through the results like this:
for (problem, groupedReport) in groupedReports {
// do something with the groupedReport
}
update
Or convert the results to an array like this:
let grouped_reports = Array(groupedReports.values)

Filter Array of [AnyObject] in Swift

I have an array of AnyObject objects in Swift. Each object has attributes of a restaurant, such as name, type, loc, etc. How can I filter the array if I want to keep all objects in the array that contain type: "Sushi".
Sample array of [AnyObject] with 2 objects. The filter should keep the first object (type: sushi):
[<Restaurant: 0x7ff302c8a4e0, objectId: LA74J92QDA, localId: (null)> {
City = "New York";
Country = "United States";
Name = Sumo Japan;
Type = Sushi, Japanese, Asian;
}, <Restaurant: 0x7ff302daa790, objectId: 0aKFrpKN46, localId: (null)> {
City = "New York";
Country = "United States";
Name = Little Italy;
Type = Italian, Pizza;
}]
Current Code (but I'm not sure if the filter can search through an array of [AnyObject]) :
var query = PFQuery(className:"Restaurant")
query.whereKey("RestaurantLoc", nearGeoPoint:userGeoPoint, withinMiles:50)
query.limit = 2
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if objects != nil {
println("list of objects of nearby")
println(objects)
let searchString = "Sushi"
let predicate = NSPredicate(format: "Type CONTAINS[cd] %#", searchString);
//Line below gives error: '[AnyObject]' does not have a member named 'filteredArrayUsingPredicate'
//let filteredArray = objects.filteredArrayUsingPredicate(predicate!)
Your array, objects, is an array of PFObject objects. Thus, to filter the array, you might do something like:
let filteredArray = objects.filter() {
if let type = ($0 as PFObject)["Type"] as String {
return type.rangeOfString("Sushi") != nil
} else {
return false
}
}
My original answer, based upon an assumption that we were dealing with custom Restaurant objects, is below:
You can use the filter method.
Let's assume Restaurant was defined as follows:
class Restaurant {
var city: String
var name: String
var country: String
var type: [String]!
init (city: String, name: String, country: String, type: [String]!) {
...
}
}
So, assuming that type is an array of strings, you'd do something like:
let filteredArray = objects.filter() {contains(($0 as Restaurant).type, "Sushi")}
If your array of types could be nil, you'd do a conditional unwrapping of it:
let filteredArray = objects.filter() {
if let type = ($0 as Restaurant).type as [String]! {
return contains(type, "Sushi")
} else {
return false
}
}
The particulars will vary a little depending upon your declaration of Restaurant, which you haven't shared with us, but hopefully this illustrates the idea.
Swift 3 Solution
Use the filter method on an array.
let restaurants: [Restaurants] = [...]
restaurants.filter({(restaurant) in
return Bool(restaurant.type == "sushi")
})
or return Bool(restaurant.type.contains("sushi")) if type is an array.
Ok, if the array objects contains only Restaurant(s) the following code does work.
Lets say Restaurant is something like this:
enum RestaurantType {
case Sushi, Japanese, Asian
}
class Restaurant {
var type = [RestaurantType]()
// more properties here...
}
First of all lets define an array of Restaurant(s).
var restaurants = objects as [Restaurant]
Then we can filter it:
var sushiRestaurants = restaurants.filter { (restaurant : Restaurant) -> Bool in
return contains(restaurant.type, .Sushi)
}
Update:
Now I am assuming objects is an array of PFObject(s)
Just ignore my previous code and try this:
var restaurants = objects as [PFObject]
var sushiRestaurants = restaurants.filter { (restaurant : PFObject) -> Bool in
return contains(restaurant["Type"], "Sushi")
}
Maybe it will crash again, the problem is that I don't know the type of Restaurant.Type. I'm trying. Maybe the next error message will provide more useful info.
Modification of Rob's answer as Swift 2.0, In swift 2.0 using Rob's code gives error as follows -
initializer for conditional binding must have optional type, not 'string'
However it can be solved by using guard statement instead of if-let as below -
let filteredArray = objects.filter() {
guard let type = ($0 as PFObject)["Type"] as String else {
return false
}
return type.rangeOfString("Sushi") != nil
}
I have a solution as given below.
func filterByCuisineType(list: [Restaurant]) -> [Restaurant]{
if self.cuisineTypes.count == 0 {
return list
}
let array: [Restaurant] = list.filter { (restaurant) -> Bool in
for cuisineName in self.cuisineTypes{
let isContained: Bool = restaurant.cuisineType.contains(cuisineName)
if isContained {
return true
}
}
return false
}
return array
}