I have an array of dictionary with custom object in swift.
Now I am comparing the object for add & update.
The logic is as simple to add the data if not exist and update if any change in dictionary.
User is custom object type:
#objc public class User: NSObject , Mappable
from the getUserID i can able to get userID
The below code is execute in for loop from where i am passing User object.
var peopleList = [User]()
if self.peopleList.count > 0 {
if self.peopleList.contains(where: {$0.getUserID() == users.getUserID()})
{
// check for any update in dist
if let index = self.peopleList.index(of: users)
{
if users.isEqual(self.peopleList[index])
{
print("equal no updates")
}
else
{
print("need to updates objects..")
}
}
//already exist room
}
else
{
self.peopleList.append(users)
}
}
I know it may be related to equatable
so I am using below fuction
func isEqual<T: Equatable>(type: T.Type, a: Any, b: Any) -> Bool? {
guard let a = a as? T, let b = b as? T else { return nil }
return a == b
}
But I am getting index = nil.
Is there any idea or suggestion to solve it.
If any other way to do it efficiently them most welcome.
I think this simplified version should work
if self.peopleList.isEmpty, let user = self.peopleList.first(where: { $0.getUserID() == users.getUserID() }) {
if user == users {
// is equal
} else {
// do update
}
} else {
self.peopleList.append(users)
}
Related
I am creating a wizard using UICollectionView with an array of CollectionViewCells:
var viewCells:[BaseCVCell] = [createEventSubjectSearch(), createEventEventForm()]
This array is dynamically added to based on a series of UISwitch's that the user controls. I can add to the array fine using the code below, however I can't seem to remove an item when a user turns the switch off.
func switchToggled(sender : UISwitch) {
if sender == createDiarySwitch {
if sender.isOn {
parentClass?.viewCells.append(createEventDeferEvent())
} else {
if let i = parentClass?.viewCells.index(where: { $0 == createEventDeferEvent() }) {
parentClass?.viewCells.remove(at: i)
}
}
}
if sender == createDeferredSwitch {
if sender.isOn {
parentClass?.viewCells.append(createEventDiariseEvent())
} else {
if let i = parentClass?.viewCells.index(where: { $0 == createEventDiariseEvent() }) {
parentClass?.viewCells.remove(at: i)
}
}
}
parentClass?.wizardCollectionView.reloadData()
}
I have tried the above code, as well as:
if let index = parentClass?.viewCells.index(of: createEventDiariseEvent()) {
parentClass?.viewCells.remove(at: index)
}
Neither approach works (no errors, the code just never returns a value). I'd like to try and avoid naming elements where possible. Is there a way to do this?
Thanks for your answers, DonMag
I've achieved the desired functionality by instanciating the two dynamic cells in the main class:
let diariseCell : createEventDiariseEvent()
and then in the loop calling as thus:
if sender == createDiarySwitch {
if sender.isOn {
parentClass?.viewCells.append((parentClass?.diariseCell)!)
} else {
if let i = parentClass?.viewCells.index(where: { $0 == parentClass?.diariseCell }) {
print("Found cell reference at index \(i)")
parentClass?.viewCells.remove(at: i)
}
}
}
Works a charm now. Amazing what another pair of eyes can pick out!
I have a structure in my Swift app:
open class Cluster : NSObject {
open var username: String? = ""
open var id: String? = ""
open var deleted: Bool? = false
}
and now I'm iterating over this array and I'm adding new elements to it, but only in case those elements are not there yet:
if(!self.array.contains(where: {$0.id==temp.id}))
{
self.array.append(temp);
}
I want to tweak this code so that it not only adds new elements if they're not there, but also removes the ones that - in the meantime - had their flag deleted changed to true.
I started writing this code:
if(!self.array.contains(where: {$0.id==temp.id}))
{
self.array.append(temp);
} else {
if(temp.deleted == true){
self.array.remove //how can I remove here this specific element?
}
}
To remove a particular element from an array, you are supposed to get index of that element first and then delete as shown below:
if let index:Int = self.array.index(where: {$0.id == temp.id && $0.deleted == true}) {
self.array.remove(at: index)
}
First, I suggest you fix your class:
An optional Bool makes no sense - the object is either deleted or not
An optional id doesn't make much sense either; All objects need an id
If you implement the hash and equality parts of NSObject then you get access to array's index(of:) method and you can use sets.
Cluster.swift
open class Cluster : NSObject {
open var username: String? = ""
open let id: String
open var isDeleted: Bool = false
init(id: String) {
self.id = id
}
open override var hashValue: Int {
get {
return self.id.hashValue
}
}
open override func isEqual(_ object: Any?) -> Bool {
guard let rhs = object as? Cluster else {
return false
}
let lhs = self
return lhs.id == rhs.id
}
}
Now, given an array of Cluster objects, you can remove the deleted ones using:
let cleanArray = dirtyArrayOfCluster.filter {
!$0.isDeleted
}
And you can remove duplicates by passing the array through a set:
let deDupedArray = Array(Set(cleanArray))
if temp.deleted == true, let index = array.index(where: { $0.id == temp.id }) {
array.remove(at: index)
}
What about this?
if array.contains(where: { $0.id == temp.id } ) {
array.append(temp)
}
array = array.filter { $0.deleted == true }
The first part add temp only if it is not into the array.
The last line removes all the elements marked as deleted.
So what im trying to do is, retrieve the object that is already saved on Realm, modify some properties and then save it again, keeping only one record on the database.
Heres my code :
Getting the Realm already saved user
if let user = User.query() {
user.latitude = String(latitude)
user.longitude = String(longitude)
user.save()
}
User query func, inside User Obj
static func query() -> User? {
return User.allObjects().toArray(User.self).first
}
func save() -> User {
RealmPersister.saveObjects([self])
return self
}
And the RealmPersister class func
class func saveObjects(objects: [RLMObject]?) {
autoreleasepool {
guard let objects = objects where objects.count > 0 else { return }
do {
let realm = RealmEncrypted.realm()
realm.beginWriteTransaction()
realm.addObjects(objects)
try realm.commitWriteTransaction()}
catch {
print("Realm did not write objects! \(objects)")
}
}
}
class func deleteObjects(objects: [RLMObject]?) {
autoreleasepool {
guard let objects = objects where objects.count > 0 else { return }
do {
let realm = RealmEncrypted.realm()
realm.beginWriteTransaction()
realm.deleteObjects(objects)
try realm.commitWriteTransaction()
} catch {
print("Realm did not delete objects! \(objects)")
}
}
}
This appears that you need to be using primary keys on the User object. This way you can retrieve the same object quickly via the primary key, modify it, and then save it without the possibility of creating duplicates.
I have a Realm Object
class CoursesModel: Object {
dynamic var courseName = ""
dynamic var par3Field = 0
dynamic var par4Field = 0
dynamic var par5Field = 0
}
When somebody enters the course name I want to check whether it already exists before writing it to Realm.
Can you please tell me what I'm doing wrong because it doesn't seem to loop through.
class func compareCourse(name : String) -> Bool {
let c = name
do
{
let realm = try Realm()
let course = realm.objects(CoursesModel)
for course in course {
if course == c {
print("course = \(course)")
print("c = \(c)")
return true
}
else {
return false
}
}
}
catch
{
// return nil
}
return false
}
Any help will be greatly appreciated.
EDIT - WORKING CODE HERE
class func compareCourse(name : String) -> Bool {
let c = name
do
{
let realm = try Realm()
let course = realm.objects(CoursesModel)
for course in course {
let a = course.courseName
print("Model Course = \(a)")
print("Passed Course = \(c)")
if a == c {
return true
}
}
}
catch
{
// return nil
}
return false
}
You are returning in both branches of the loop, which immediately exits out of the function. You do not want to return false on the first failure, but only after all have failed (I think).
I want to know how I could store the entire custom column (the user Pointer<_User> column from a custom class) and put them all in an array variable so that I can see if a the user exists in that class or not. This is what I have:
Old Code
var objectUserIdArray = [String]()
let objectUserIdQuery : PFQuery = PFQuery(className: "Scores")
objectUserIdQuery.findObjectsInBackgroundWithBlock {
(objects : [PFObject]? , error : NSError?) -> Void in
var objectID = objects! as [PFObject]
for i in 0..<objectID.count {
objectUserIdArray.append(objectID[i].objectId!)
}
for _ in objectID {
print(objectUserIdArray)
}
New Code
func saveScoresOnParse() {
objectUserIdQuery.whereKey("User", equalTo: PFObject(withoutDataWithClassName: "_User", objectId: userID))
objectUserIdQuery.findObjectsInBackgroundWithBlock {
(objects : [PFObject]? , error : NSError?) -> Void in
if error == nil {
//var objectID = objects! as [PFObject]
/*for i in 0..<objectID.count {
self.objectUserIdArray.append( objectID[i].objectId! )
}*/
for _ in objects! {
print(objects)
}
// The score key has been incremented
for (var i = 0 ; i < self.objectUserIdArray.count ; i++) {
if self.userID != objects![i] {
print("New Scores")
print("R: \(self.rightAnswers)")
print("W: \(self.wrongAnswers)")
print("S: \(self.skippedQuestions)")
self.scores["User"] = PFUser.currentUser()
self.scores["Right"] = self.rightAnswers
self.scores["Wrong"] = self.wrongAnswers
self.scores["Skipped"] = self.skippedQuestions
self.scores.saveInBackground()
} else if self.userID == objects![i] {
print("Updated Scores")
self.scores.incrementKey("Right", byAmount: 1)
self.scores.incrementKey("Wrong", byAmount: 1)
self.scores.incrementKey("Skipped", byAmount: 1)
print("R: \(self.rightAnswers)")
print("W: \(self.wrongAnswers)")
print("S: \(self.skippedQuestions)")
self.scores.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// The score key has been incremented
} else {
// There was a problem, check error.description
}
}
} else {
print("Error")
}
}
} else {
print(error)
}
}
But it only stores the objectId column and not the Pointer<_User> column. I know this because when I print the stuff that is inside, it prints out the objectIds.
This is what happens, instead of just updating the current user's scores, it just makes new ones. I want the if statement to check if the user already exists in that column and if it does updates the scores and if it doesn't, make new ones. (The new code's if statement doesn't work, i have to bring it out for it to save...)
Your updated question make clearer what you are actually wanting to do;
Save or update a user's scores in your Parse Score object. To do this, there is no reason to retrieve any object Ids or loop through any results. More often than not you don't do use Object Ids explicitly when using Parse; you can simply pass the object itself with Parse working out the references for you.
I am not sure how you exactly want to change the scores; in your code above you increment in one case but set the scores explicitly in another, but the code below shows the general approach.
If you are frequently or repeatedly going to update a score record then you could make your code more efficient by holding a reference to the Scores object in a property after you find it the first time and simply update & save it subsequently.
func saveScoresOnParse() {
if let currentUser=PFUser.currentUser() {
let scoreQuery= PFQuery(className: "Scores")
scoreQuery.whereKey("User",equalTo:currentUser)
scoreQuery.getFirstObjectInBackgroundWithBlock {
(object : PFObject? , error : NSError?) -> Void in
if error == nil {
var scoreObject=object ?? PFObject.objectWithClassName("Scores")
if (scoreObject["User"]==nil) {
scoreObject["User"]=currentUser
}
scoreObject["Right"]=self.rightAnswers
scoreObject.saveInBackground()
} else {
print(error)
}
}
} else {
print("No current user!")
}
}