Swift Array Objects Invalidated During Delete - realm

Swift 3, Xcode 8, Realm 2.0.0
I have a Realm object that has a List property called like this:
class Entry: Object{
let approaches = List<Approach>()
//...
}
I pull an Entry object that has an Approach list in it so that entry.approaches is a list of approaches.
I then load up my own separate array with the list so that I can manipulate its contents using Approach objects.
var approaches = [Approach]()
for approach in entry.approaches{
approaches.append(approach)
}
Prior to saving some edits to the Entry, I want to remove all existing approaches and replace them with new elements I put in the approaches array elsewhere in my code.
try realm.write {
print(approaches) //This prints out my Approach objects
realm.delete(entry.approaches) //Clear out existing items in list
print(approaches) //-!- This prints [[invalid object], [invalid object]]
}
If I manually put the Realm objects in my own approachesarray, why are they invalidated when entry.approaches is deleted?
Is there a better way to replace all objects in a list than this?

It's intended behavior. Maybe you are confusing deleting objects from Realm and removing objects from List. realm.delete() execute the former. The objects were deleted from Realm. Then all references pointed to the objects would be invalidated.
If you'd like to clear entry.approaches, not deleting from Realm, you can use List.removeAll() method.
try realm.write {
entry.approaches.removeAll()
}
FYI: Copying all list items to array can be written like the following. No need to iterate list.
let approaches = [Approach](entry.approaches)

Related

How do I dynamically remove an element from a QML basic type list?

I've successfully added elements using
list.push(element)
But how do I remove them? I've tried the following, but none of them seem to work.
list.pop()
list.pop_front()
list.remove()
list.remove(int)
list.remove(element)
For people who always keep ending up on this page, like I did: I found a very hacky solution to remove an element from a list<QtObject>.
myList = Array.from(myList).filter(r => r !== elementIWant2Remove)
This is not very elegant, but it does the trick, if you need to remove an element.
You can't remove individual items. According to the docs:
Note that objects cannot be individually added to or removed from the list once created; to modify the contents of a list, it must be reassigned to a new list.
This means that the pop method most likely won't exist with the basic list type. (However, there is a push method.)
You'll be much better off using JavaScript arrays.
property var myArray: [1, 2, 3]
Component.onCompleted: {
myArray.push(4);
myArray.push(5);
myArray.splice(1, 1) // simulates a remove
myArray.pop(); // pop last item
console.debug(myArray) // [1, 3, 4]
}
I've used ints here, but you can also store QML objects and other types as well. Using JavaScript arrays exposes your variables to most (if not all) of JavaScript's Array functions.

Firestore Update fields in nested objects with dynamic key

I need to update a field in a nested object with a dynamic key.
the path could look like this: level1.level2.DYNAMIC_KEY : updatedValue
The update-method deletes everything else on level1 instead of only updating the field in the nested object. The update() acts more like a set(). What am I doing wrong?
I tried the following already:
I read the documentation https://firebase.google.com/docs/firestore/manage-data/add-data#update-data
but that way it is a) static and b) still deletes the other fields.
Update fields in nested objects
If your document contains nested objects, you can use "dot notation" to reference nested fields within the document when you call update()
This would be static and result in
update({
'level1.level2.STATIC_KEY' : 'updatedValue'
});
Then I found this answer https://stackoverflow.com/a/47296152/5552695
which helped me to make the updatepath dynamic.
The desired solution after this could look like
field[`level1.level2.${DYNAMIC_KEY}`] = updateValue;
update(field);
But still: it'll delete the other fields in this path.
UPDATE:
The Structure of my Doc is as follows:
So inside this structure i want to update only complexArray > 0 > innerObject > age
Writing the above path into the update() method will delete everything else on the complexArray-level.
A simple update on first-level-fields works fine and lets the other first-level-fields untouched.
Is it possible, that firestore functions like update() can only act on the lowest field-level on an document. And as soon as i put complex objects into an document its not possible to select such inner fields?
I know there would be the solution to extract those "complex" objects into separate collections + documents and put these into my current lowest document level. I think this would be a more accurate way to stick to the FireStore principles. But on Application side it is easier to work with complex object than to always dig deeper in firestore collection + document structure.
So my current solution is to send the whole complex object into the update() method even though I just changed only one field on application side.
Have you tried using the { merge: true } option in your request?
db
.collection("myCollection")
.doc("myDoc")
.set(
{
level1: { level2: { myField: "myValue" } }
},
{ merge: true }
)

Kotlin: Modifying (immutable) List through cast, is it legitimate?

As we know the List in Kotlin is immutable i.e. you can't do add and remove as below.
class TempClass {
var myList: List<Int>? = null
fun doSomething() {
myList = ArrayList<Int>()
myList!!.add(10)
myList!!.remove(10)
}
}
But if we cast it to ArrayList as below, the add and remove works.
class TempClass {
var myList: List<Int>? = null
fun doSomething() {
myList = ArrayList<Int>()
(myList!! as ArrayList).add(10)
(myList!! as ArrayList).remove(10)
}
}
I just thought this is odd, as myList is really a List, which is suppose to be immutable. And casting it, allow it to be altered.
Is what done above (casting to Array and modify the content) legitimate, or the language need to improve to disallow that?
There are a few different types of immutability:
One is mentioned from a separate SO answer here.
Readonly - you are NOT supposed to change it (Kotlin's List) but something may (cast to Mutable, or change from Java).
List is just an interface that does not have mutating methods, but you can change the instance if you cast it to MutableList.
Someone then goes on to comment that Kotlin chose to be readonly in order to use Java collections directly, so there is no overhead or conversion in using Java collections.
Kotlin List is readonly, not immutable. Other callers (Java for example) may change the list. Kotlin callers might cast the list and change it. There is no immutable protection.
Original Source: Kotlin and Immutable Collections?
Is it legitimate? Well, yes. There are uses cases in which that would make sense.
Is it a good idea? I think not, especially if you're talking about casting a list that was returned by some external library. The cast will fail if someone actually hands you some List implementations that really is immutable and does not implement MutableList. The fact that at the moment (Kotlin 1.0.2), all of Kotlin's Lists are also MutableLists doesn't mean that every List you'll ever see in your code is also an MutableList.
Right now if you use listOf() you'll get a List with all methods, which mutate the list, throwing java.lang.UnsupportedOperationException:
val list = listOf(1, 2)
val mlist = list as MutableList
mlist.add(3)
This throws:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)

Firebase / AngularFire limited object deletes its properties on $save()

I have a primary node in my database called 'questions', when I create a ref to that node and bring it into my project as a $asObject(), I can modify the individual questions and $save() the collection without any problems, however as soon as I try to limit the object, by priority, the $save() deletes everything off of the object!
this works fine:
db.questions = $firebase(fb.questions).$asObject();
// later :
db.questions.$save();
// db.questions is an object with many 'questions', which I can edit and resave as I please
but as soon as I switch my code to this:
db.questions = $firebase(fb.questions.startAt(auth.user.id).endAt(auth.user.id)).$asObject();
// later :
db.questions.$save();
// db.questions is an empty firebase object without any 'questions!'
Is there some limitation to limited objects (pun not intended) and their ability to be changed and saved?? The saving actually saves updates to the questions to the database, but somehow nukes the local $firebase object...
First line of synchronized arrays ($asArray) documentation:
Synchronized arrays should be used for any list of objects that will be sorted, iterated, and which have unique ids.
First line of synchronized objects ($asObject) documentation:
Objects are useful for storing key/value pairs, and singular records that are not used as a collection.
As demonstrated, if you are going to work with a collection and employ limit, it would behoove you to use a tool designed for collections (i.e. $asArray).
If you were to recreate the behavior of $save using the Firebase SDK, it would look like this:
var ref = new Firebase(URL).limit(10);
// ref.set(data); // throws an error!
ref.ref().set(data); // replaces the entire path; same as $save
Thus, the behavior here exactly matches the SDK. You cannot, technically, call set() on a query instance and this doesn't make any sense, really. What does limit(10) mean to a JSON object? If you call set, which 10 unordered keys should be set? There is no correlation here and limit() really only makes sense with a collection of data, not a list of key/value pairs.
Hope that helps.

Deleting items in foreach

Should you be allowed to delete an item from the collection you are currently iterating in a foreach loop?
If so, what should be the correct behavior?
I can take quite a sophisticated Collection to support enumerators that track changes to the collection to keep position info correct. Even if it does some compromisation or assumptions need to be made. For that reason most libraries simply outlaw such a thing or mutter about unexpected behaviour in their docs.
Hence the safest approach is to loop. Collect references to things that need deleting and subsequently use the collected references to delete items from the original collection.
It really depends on the language. Some just hammer through an array and explode when you change that array. Some use arrays and don't explode. Some call iterators (which are wholly more robust) and carry on just fine.
Generally, modifying a collection in a foreach loop is a bad idea, because your intention is unknown to the program. Did you mean to loop through all items before the change, or do you want it to just go with the new configuration? What about the items that have already been looped through?
Instead, if you want to modify the collection, either make a predefined list of items to loop through, or use indexed looping.
Some collections such as hash tables and dictionaries have no notion of "position" and the order of iteration is generally not guaranteed. Therefore it would be quite difficult to allow deletion of items while iterating.
You have to understand the concept of the foreach first, and actually it depends on the programming language. But as a general answer you should avoid changing your collections inside foreach
Just use a standard for loop, iterate through the item collection backwards and you should have no problem deleting items as you go.
iterate in reverse direction and delete item one by one... That should proper solution.
No, you should not. The correct behaviour should be to signal that a potential concurrency problem has been encountered, however that is done in your language of choice (throw exception, return error code, raise() a signal).
If you modify a data structure while iterating over its elements, the iterator might no longer be valid, which means that you risk working on objects that are no longer part of the collection. If you want to filter elements based on some more complex notation, you could do something like this (in Java):
List<T> toFilter = ...;
List<T> shadow;
for ( T element : toFilter )
if ( keep(element) )
shadow.add(element);
/* If you'll work with toFilter in the same context as the filter */
toFilter = shadow;
/* Alternatively, if you want to modify toFilter in place, for instance if it's
* been given as a method parameter
*/
toFilter.clear();
toFilter.addAll(shadow);
The best way to remove an item from a collection you are iterating over it to use the iterator explitly. For example.
List<String> myList = ArrayList<String>();
Iterator<String> myIt = myList.iterator();
while (myIt.hasNext()) {
myIt.remove();
}

Resources