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

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

Related

No exact matches in call to instance method 'append' in swift

Tried to combine or merging two model to one model
1st model = items [ InboxModel]. (My own Inbox)
2nd model = items2 [MoInboxModel] (SDK Inbox)
1st + 2nd -> combinedItems
private var items: [InboxModel] = []
private var items2: [MoInboxModel] = []
private var combinedItems: [combinedInboxModel] = []
struct InboxModel {
let id: String
let title: String
let message: String
let date: Date
}
struct MoInboxModel {
let id: String
let title: String
let message: String
let date: Date
}
struct combinedInboxModel {
let id: String
let title: String
let message: String
let date: Date
}
self?.combinedItems.append(self?.items). //No exact matches in call to instance method 'append
self?.combinedItems.append(contentsOf: self?.items2 ?? []) //No exact matches in call to instance method 'append
Why there is an error while merge it ? How to merge it correctly?
You have three unrelated types - InboxModel, MoInboxModel and combinedInboxModel (Which should be CombinedInboxModel. Even though they all have properties with the same name, they are different types.
There is no append function on an array of combinedInboxModel that accepts an array of InboxModel or MoInboxModel.
You could use map on each of your two input arrays to convert them to an array of CombinedInboxModel which you can then put into combinedItems.
Presumably you are writing this code in a closure, which is why you have a weak self. Best to deal with that first and then process your arrays.
guard let self = self else {
return
}
self.combinedItems = self.items.map { CombinedInboxModel(id:$0.id,title:$0.title,message:$0.message,date:$0.date) }
let items2 = self.items2.map { CombinedInboxModel(id:$0.id,title:$0.title,message:$0.message,date:$0.date) }
self.combinedItems.append(contentsOf:items2)
You haven't shown where items and items2 come from; Is it possible just to fetch them as instances of the same struct to start with?
The fact that you have three structs with the same properties is a bit fishy. I would consider a different design if I were you.
However, if you must go with this approach, you might want to consider starting with a protocol and getting rid of the combinedInboxModel struct.
protocol InboxModelable {
var id: String { get }
var title: String { get }
var message: String { get }
var date: Date { get }
}
Now make your two structs conform to InboxModelable.
struct InboxModel: InboxModelable {
let id: String
let title: String
let message: String
let date: Date
}
struct MoInboxModel: InboxModelable {
let id: String
let title: String
let message: String
let date: Date
}
Since both of your types conform to InboxModelable you can directly store both types in an array of type Array<InboxModelable> without having to map the elements.
class SomeClass {
private var items: [InboxModel] = []
private var items2: [MoInboxModel] = []
private var combinedItems: [InboxModelable] = []
func combineItems() {
doSomething { [weak self] in
guard let self = self else { return }
self.combinedItems.append(contentsOf: self.items)
self.combinedItems.append(contentsOf: self.items2)
}
}
}

How to retrieve data held in an array from Firestore using Swift

this seems like it should be quite a simple thing to do, but can't seem to find a solution. I have some data in Firestore held in an array that I need to get and place into and two dimensional array inside my swift app. I'm pretty noob so apologies in advance!
This is the data I'm trying to get from Firestore
This is the code I'm using to query my documents and then loop through the result, extracting the data
fireStoreDatabase.collection("Posts").whereField("postedTo", arrayContains: userId).order(by: "date", descending: true).addSnapshotListener { (snapshot, error) in
if error != nil {
print(error?.localizedDescription)
} else {
if snapshot?.isEmpty != true && snapshot != nil {
print("Posted data got")
//clear the arrays to stop duplicates when we do an upload and pull in the data again
self.postedShoutTextArray.removeAll(keepingCapacity: false)
self.postedByArray.removeAll(keepingCapacity: false)
self.postedDateArray.removeAll(keepingCapacity: false)
self.postedQuestionsArray.removeAll(keepingCapacity: false)
//loop through the firestore
for document in snapshot!.documents {
//add data to the arrays
if let shoutText = document.get("shoutText") as? String {
self.postedShoutTextArray.append(shoutText)
}
if let postedBy = document.get("postedBy") as? String {
self.postedByArray.append(postedBy)
}
if let date = document.get("date") as? String {
self.postedDateArray.append(date)
}
if let pollQuestions = document.get("pollQuestions") as? [String] {
self.postedQuestionsArray = pollQuestions
} else {
print("no array data")
}
self.receivedCollectionView.reloadData()
}
} else {
print("GET POSTED MESSAGES no data")
}
}
}
So I'd like the data to go into a two dimensional array (if that's possible) containing the data from the pollQuestions array for each document I loop through (does that make sense?).
Been searching all over, and seen references to map etc, but had no luck finding a solution.
Any help appreciated!
Rather than storing each property in a separate array, you may want to consider representing it with a struct. Something like:
struct Item {
var shoutText: String?
var postedBy: String?
var date: String?
var pollQuestions : [String]
}
Then, on your view controller, declare a property:
var items: [Item] = []
Then, in your snapshot listener, you can populate that array:
func getData() {
Firestore.firestore().collection("Posts").whereField("postedTo", arrayContains: userId).order(by: "date", descending: true).addSnapshotListener { (snapshot, error) in
if error != nil {
print(error?.localizedDescription)
} else {
if let snapshot = snapshot, !snapshot.isEmpty {
print("Posted data got")
self.items = snapshot.documents.map { document in
Item(shoutText: document.get("shout") as? String,
postedBy: document.get("postedBy") as? String,
date: document.get("date") as? String,
pollQuestions: document.get("pollQuestions") as? [String] ?? [])
}
self.receivedCollectionView.reloadData()
} else {
print("GET POSTED MESSAGES no data")
}
}
}
}
Later, you can access this data:
self.items[itemIndex].pollQuestions[pollQuestionIndex]

SwiftUI - Return values from array of structs are all in one row

I am calling a function in order to do a select statement in a bundled SQLite database. The function returns an array of structs. The database is being read correctly as I have put some print commands in the code. However the final array only has 1 row in it, which contains all the data, instead of 16 rows of structs.
The struct code, which is in databaseHelper.swift, is...
struct ButtonData: Hashable {
let english: String
let categoryID: Int
let indonesian: String
}
The database code, in databaseHelper, is
class DatabaseHelper {
var buttonVars = [ButtonData]()
var database: Connection!
let buttonsTable = Table("Button")
let english = Expression<String>("english")
let category = Expression<String>("category")
let categoryID = Expression<Int>("ID")
let filename = Expression<String>("filename")
let indonesian = Expression<String>("indonesian")
init() {
do {
let path = Bundle.main.path(forResource: "sga", ofType: "db")!
let database = try Connection(path, readonly: true)
self.database = database
print("Database initialized at path \(path)")
} catch {
print("error")
}
}
func queryDatabase(passedCategory: String) -> [ButtonData] {
do {
let buttons = try self.database.prepare(self.buttonsTable.filter(self.category==passedCategory))
for row in buttons {
print("English: \(row[self.english]), ID: \(row[self.categoryID]), Indonesian: \(row[self.indonesian])")
// buttonVars.append(ButtonData(english: row[english], categoryID: row[categoryID], indonesian: row[indonesian]))
buttonVars.append(ButtonData(english: row[english], categoryID: row[categoryID], indonesian: row[indonesian]))
}
} catch {
print(error)
}
print(buttonVars[0])
print(buttonVars[1])
print(buttonVars[2])
print(buttonVars[3])
print(buttonVars[4])
print(buttonVars[5])
print(buttonVars[6])
print(buttonVars[7])
print(buttonVars[8])
print(buttonVars[9])
print(buttonVars[10])
print(buttonVars[11])
print(buttonVars[12])
print(buttonVars[13])
print(buttonVars[14])
print(buttonVars[15])
return buttonVars
}
}
The function code, which is in SoundPageView.swift (this page calls the database function), is...
func getArrayValues() {
let buttonRows = [DatabaseHelper().queryDatabase(passedCategory: category)]
let btnCount: Int = buttonRows.count
print(btnCount)
print(buttonRows[0])
}
The print values in the console show me that btnCount = 1 but before the array is returned, it is made of 16 rows. It is only after it is returned that it is reduced to 1 row.
Can anyone tell me what I'm doing wrong? I don't know how to access the data. Thanks.
I'm not sure why you are putting the brackets in this call:
let buttonRows = [DatabaseHelper().queryDatabase(passedCategory: category)]
The func queryDatabase returns an array on its own. I think it should be this:
let buttonRows = DatabaseHelper().queryDatabase(passedCategory: category)
Otherwise your result will be an array with one entry, which is the result of the call to queryDatabase.

Add Parse array to dictionary swift

I have some objects in parse and I am getting the data successfully as [PFObjects]. The issue is that I am trying to add the array elements [PFObjects] to a dictionary as values. But I keep getting an empty dictionary, so the values are not added to the dictionary. The dictionary count is also 0.
This is what I tried so far:
var postDictionary = [String:[AnyObject]]()
query.findObjectsInBackground(block: { (posts: [PFObject]?, error:Error?) in
if let unwrappedPosts = posts {
for posts in unwrappedPosts {
if let postText = posts.object(forKey: "title") as?String {
self.titleArray.append(postText)
print("count", self.titleArray.count) // count 10
self.postDictionary["title"]?.append(self.titleArray as AnyObject)
**try to force unwrap **
self.postDictionary["title"]!.append(self.titleArray as AnyObject), and the app crashed
for (title, text) in self.postDictionary {
print("\(title) = \(text)")
}
print("Dictionay text count",self.postDictionary.count) // count is 0
}
}
}
})
This syntax is very confusing
self.titleArray.append(postText)
self.postDictionary["title"]?.append(self.titleArray as AnyObject)
You append a string to an array and then you are going to append the array to the array in the dictionary. I guess this is not intended.
I recommend to map the title strings and set the array for key title once
var postDictionary = [String:[String]]()
query.findObjectsInBackground(block: { (posts: [PFObject]?, error:Error?) in
if let unwrappedPosts = posts {
self.titleArray = unwrappedPosts.compactMap { $0.object(forKey: "title") as? String }
self.postDictionary["title"] = self.titleArray
for (title, text) in self.postDictionary {
print("\(title) = \(text)")
}
print("Dictionay text count",self.postDictionary.count) // count is 0
}
})
Never use AnyObject if the type is more specific.
The proper way of adding to a dictionary is using updateValue because as far as i can see that you don't have the key "title" in your dictionary and you are appending values to unknown key i guess.
This should help:
titleArray.append(postText)
postDictionary.updateValue(titleArray as [AnyObject], forKey: "title")
for (key,value) in postDictionary {
print("\(key) \(value)")
}
Finally this should print:
title [posts1, posts2, posts3]

Filtering arrays for use with UISearchBar

I have a table view which displays a user's Name, Company Name and Photo (PFFile). Each tableView row I have has all of this information in it.
I am using UISearchBarDelegate and IB to implement a search function to filter by the user's Name. It is finding the correct user but I have not been able to also update the company photo.
How do I filter the other arrays? The items I need from the arrays will be at the same index as the ones taken from the user's Name array.
EDIT: I am trying a different data structure and am receiving array index out of range, updated code below:
var filterArray = [User]() //<-- globally declared
var userArray = [User]() //< Global
class User {
var name: String?
var company: String?
init (name: String?, company: String?) {
self.name = name
self.company = company
}
}
//In a class which populates the search arrays
for object in unwrappedSucceeded {
let username = object.valueForKey("username") as! String
let companyName = object.valueForKey("companyName") as! String
let user = User(name: username, company: companyName)
userArray.append(user)
}
//tableViewController
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
filterArray.removeAll(keepCapacity: false)
if searchText.characters.count != 0 {
isSearch = true
self.search(searchText)
} else {
isSearch = false
}
}
func search(text: String) -> Void {
filterArray = userArray.filter({$0.name == text})
}
//In cellForRowAtIndexPath
cell.usernameCell.text = filterArray[indexPath.row].name //ARRAY INDEX OUT OF RANGE
Like I said you strongly recommend to group each user's info into one big container, therefore we could use array of struct or class, then it comes easier to filter.
schematic for the container:
struct Container
{
var username:String?
var companyName:String?
var photo:UIImage?
}
your main array will be : var arrayofData = [Container]()
Now when you are query your objects from parse, inside of your query function
// after you called the findObjectsWithBackgroundBlock()
// let's assume you check for error and if the [PFObject] is empty or not
for one in objectsFromParse
{
let photoToget = one["Photo"] as! PFFile
// next step should be to get the image data right :)
{
// let's assume that is the block when get the image data right:)
// check your data and assign it to some UIImage
// then
let userRepresentation = Container() //<-- we are creating a single object representation for each user
let username = one["username"] as! String //<--data we got from Parse
let companyName = one["companyName"] as! String
let userImage = //the UIImage which contains the data
userRepresentation.username = username
userRepresentation.companyName = companyName
userRepresentation.photo = userImage
// then we append
arrayOfData.append(userRepresentation)
}
}
Now we have all data into our array, so let's filter by username and also I hope you configure your tableView so when you have data from filter or regular array.
var filterArray = [Container]() //<-- globally declared
func search(text: String) -> Void
{
filterArray = arrayOfData.filter(){ (Container) -> Bool in
let range = Container.name!.rangeOfString(text, options:NSStringCompareOptions.CaseInsensitiveSearch) return range != nil }
// then you are good to go
}
let arr1 = [10,20,40]
let e1 = arr1.enumerate()
let arr2 = ["a","b","c"]
let f1 = e1.filter { $0.element % 20 == 0 }
let f2 = arr2.enumerate().filter { j, _ in
f1.contains { i, _ in
i == j
}
}
print(f1.map{$0.element}, f2.map{$0.element})
// [20, 40] ["b", "c"]
now you have both arrays "filtered". the best, what you can do is redesigning your data model!

Resources