I'm trying to be notified when an array changes its content.
Through this code I'm able to notify the setting of the array, but nothing happens when a new item is inserted.
var array: MutableMap<String, List<String>> = mutableMapOf()
set(value) {
field = value
arrayListener?.notify()
}
The only thing I came up with is resetting the array to itself everytime I add, delete o edit items, like this:
array = array
I read this question How to watch for array changes? relative to Javascript, but I'd like an easier solution then creating a new object, can anyone suggest it?
Array's API is quite simple: elements can be written there and can be read from an array.
At 99% (a number without justification, read "the vast majority") array's usages people are satisfied with this simple API. It would be a shame if a simple interface with straightforward implementation was mixed with tricky functionality.
Moving to your problem, a possible approach could be create an array's wrapper
class ArrayWrapper<T> (private val array: Array<out T>,
private val onChange: () -> Unit) {
val size = array.size
fun get(index: Int): T {
return array[index]
}
fun set(index: Int, value: T) {
array[index] = value
onChange()
}
}
An example of usage:
val ints = ArrayWrapper(arrayOf(1, 2, 3)) {
println("Array has been changed")
}
You are currently only observing that a new map is assigned to array variable. Your code won't notify you if the map entry is added or removed from the map.
If you want to observe if the array is reassigned you can use an Observable delegate from Kotlin standard lib.
Note: You should rename array variable that it fits a data structure you have used.
Here is an example:
var map: MutableMap<String, List<String>> by Delegates.observable(mutableMapOf()) {
property, oldValue, newValue ->
if (oldValue != newValue) //notify that reference has changed
}
You can read about observable delegate here.
Since you want to observe changes in the map I think you should take a look at this question. It might help. To archive what you want, you'll have to extend map or create a wrapper around it which will notify you when a map entry is added or removed.
Related
let mut args: Vec<String> = args().collect();
for mut i in 0..args.len() {
if args[i].contains("-type") {
project_type = args[i].split("=").last().unwrap();
args.remove(i);
i-=1;
}
}
I'm very new to rust and want to know why this code gives me the error "cannot borrow 'args' as mutable because it is also borrowed as immutable" and how i can fix it
I'm trying to check if the args Vec contains an item like "-type=some_type", then put "some_type" in a variable called "project_type". Finally I want to remove the item "-type=some_type" from the list and keep searching.
edit: if I'm doing what I'm trying to do wrong, i would appreciate a better solution.
str::split doesn't create new string objects, it returns references to the existing one. So your project_type variable is a reference to a string in args. When you remove from args, you invalidate all of those references.
You can convert a &str to a String and break the reference with .to_owned()
project_type = args[i].split("=").last().unwrap().to_owned();
Now, for loops in Rust iterate over data structures. As such, your i -= 1 trick won't work. i will just get reset back to where it was at the next iteration. If you want to do this the way you're currently doing it, you want a while loop.
let mut i = 0
while i < args.len() {
if ... {
...
} else {
i += 1;
}
}
However, let's think about this a bit more functionally. What it sounds like you want is to find a matching element of a vector (and its index) and then do something with it. So let's break that down into two separate problems.
We use enumerate to get a collection together with its index, and find to search it. Since it looks like you expect the value to be there, we'll unwrap it and panic if it's not there. If you want to handle the error, convert this to a match.
let (idx, element) = args.iter().find(|x| x.contains("-type")).unwrap();
Once we have it, we can do our work on element to get the project type.
project_type = element.split("=").last().unwrap().to_owned();
Note that at this point project_type does not depend on args at all. element does, and Rust may be smart enough to see this, but let's just be perfectly safe and make sure element gets dropped before we modify args. Once there are no more references to args, we can mutably borrow to do the remove operation.
let idx = {
let (idx, element) = args.iter().enumerate().find(|(_, x)| x.contains("-type")).unwrap();
project_type = element.split("=").last().unwrap().to_owned();
idx
}
args.remove(idx);
I have an array of value pairs I want to modify. I need to add and remove values from this array as well, so I used a list. When I tried to use a list, I encountered an error.
Error CS1612 - Cannot modify the return value of 'List<(int, float)>.this[int]' because it is not a variable
So I decided I would investigate. I tried using an array instead, and it... worked fine? The following code only throws an error on arr1[0].Item1 += 1;.
static void Main()
{
List<(int, float)> arr1 = new List<(int, float)>() { (0, 0) };
(int, float)[] arr2 = new (int, float)[1];
arr1[0].Item1 += 1; // This line
arr2[0].Item1 += 1;
}
Why are tuple arrays mutable, but lists are not? Is this because arrays are simple blocks of data you can modify easily, but lists have a lot of backend behind them that complicates things? Is there a simple way to get around this, or am I going to have to make my own custom class?
Why are tuple arrays mutable, but lists are not?
The list itself is mutable, but not in the way you're doing it. Note that this isn't anything specific to tuples - it's just the case for any mutable struct.
The list indexer getter returns a value (i.e. a copy of the tuple in your case) - so modifying that value wouldn't modify the copy in the list. The compiler is trying to avoid you making a change to a value that's about to be thrown away. Array access doesn't do that - arr2[0] refers to the variable within the array. (An array is effectively a collection of variables.)
If you want to mutate the list, you can have to fetch the tuple, mutate it, then put it back:
var tuple = arr1[0];
tuple.Item1++;
arr1[0] = tuple;
Note that this also explains why you can't use list access expressions as arguments for ref parameters, but you can do the equivalent for arrays:
public void Method(ref int x) => x++;
public void CallMethod()
{
var list = new List<int> { 0 };
var array = new int[] { 0 };
Method(ref list[0]); // Error
Method(ref array[0]); // Valid
}
I have these two functions where i am trying to modify the elements. One of them compiles and other says 'val cannot be reassigned'. What is the difference between the following functions? Why does one compile and the other does not?
The one that compiles
fun <T> Array<T>.mapInPlace2(transform: (T) -> T) {
for (i in this.indices) {
this[i] = transform(this[i])
}
}
The one that says
Val cannot be reassigned
fun <T> Array<T>.mapInPlace1(transform: (T) -> T) {
for (i in this) {
i = transform(i);
}
}
Similiarly to how function parameters are final in Kotlin, so are the variables used in for loops. Essentially, writing down...
for (i in array) {
...
}
... is the equivalent of doing this in Java:
for (final int i : array) {
...
}
This helps catch some common errors, in this case - if the compiler allowed you - you'd be reassigning this local variable that just contains a reference to the real element, without changing the array. In Java terms, you'd be doing this:
for (int i : array) {
i = transform(i);
}
This new value of i is unused, it doesn't change the array itself, and it will be immediately overwritten by the value of the next element when the loop comes around.
Try this:
for(i:Int in 0 until this.size) {
this[i] = "your value"
}
You're confusing the mutability of references to objects and objects themselves.
In the first example, the structure is mutable, but the reference to it is immutable. You can change the structure of the object itself, but you can't change what structure the references points to.
In the second example, you're trying to change an immutable reference to an object, not the object itself.
If you write
val i = obj
obj can still be mutated if it's a mutable object. i can't be reassigned though, sine the reference can't be changed.
I have an array myArr where I keep objects named item1, item2, item3... etc.
trace(myArr[myRandomNumber2][myRandomNumber1]); this randomly returns [object Item1], [object Item2] etc.
I need to make If statement somthing like this:
if (myArr[myRandomNumber2][myRandomNumber1] == Item7) {
//do something
}
But this doesn't work. I don't get any errors, just nothing happens. How to check current elements name?
You cannot compare two objects directly using equality operator (either == or ===). The equality operator looks to the reference address if you are using it on objects.
Each time you create a new object, your variable (or the array) holds the reference address to the newly created object.
Ofc the reference address aren't equal, that's why you are getting false result. You can compare objects in two ways, for me, the first one is preferred.
Here below is an example of an class to create objects with ;
public class MyObject {
public var name:String;
public var info:String;
public function MyObject(newName:String, newInfo:String) {
this.name = newName;
this.info = newInfo;
}
}
1. Define a custom compareTo() method,
Add the next method in your class, here in MyObject,
public function CompareTo(otherObject:MyObject):Boolean {
// check properties and compare each properties
return (this.name == otherObject.name && this.info == otherObject.info);
}
This will only return true if the properties are equal. You might use the compare operator with strings. Just expand the above with all properties. If your properties are not public, please use the getters to obtain the property values from other objects. For the current object, this. is enough.
Then in your code,
if (myArr[myRandomNumber2][myRandomNumber1].compareTo(Item7)) {
//do something
}
or
if (Item7.compareTo(myArr[myRandomNumber2][myRandomNumber1])) {
//do something
}
both does the same.
2. use the compare() method from ObjectUtil class
Please refer first to this documentation and the explanation (with example) on it.
Then check the next statement
ObjectUtil.compare(myArr[myRandomNumber2][myRandomNumber1], Item7)
This will compare both objects using the properties in the Item object. Please note that this doesn't work if you have a class as a property. You can add a depth option to the above method to do a deep comparison. Still it might be a bit inaccurate if you have polymorph objects.
The result of the above method is an integer, either -1 or 0 or 1. Please check the documentation to understand what these numbers represents. You needs the 0 because that says that the compared objects are both equal.
Then your code;
if (ObjectUtil.compare(myArr[myRandomNumber2][myRandomNumber1], Item7) == 0) {
//do something
}
That's how you can start to compare objects correctly.
By default, trace of object with undeclared toString() method writes [object ClassName] in output. So if I understand your description correctly, you can use this:
if (myArr[myRandomNumber2][myRandomNumber1] is Item7) {
//do something
}
If Item7 is not a class as it seems, and not a reference to some object stored in your array, and not an instance of such types as int, Number, String etc., then you can declare your custom comparison function to compare this objects, for example:
if(Item7.compare(myArr[myRandomNumber2][myRandomNumber1])) {
// do someting
}
And in the class of Item7:
// ...
public function compare(obj:*):Boolean {
// your logic here
}
In any other case, your code is just incorrect.
I created a simple class with one field. class Test{int value;}
If I use the "preserve references" feature and set it to "all" (i.e. both objects and arrays), then when I simply serialize an array of Test objects, it gets serialized as a JSON object with a special "$values" member with the array values, along with the expected "$id" property to preserve the array reference. That much is fine, but once again the whole thing breaks on deserialization.
Stepping through the source code, I discovered that simply because the test for "IsReadOnlyOrFixedSize" is true, it sets a flag "createdFromNonDefaultConstructor" to true, which doesn't even make any sense, because although it is a fixed size array, it is created from a default constructor, unless it considers any fixed size array constructor a non-default constructor. The bottom line is that it should be able to handle something so basic, and yet it throws this error: "Cannot preserve reference to array or readonly list, or list created from a non-default constructor".
How can I deserialize a basic array while preserving all references in JSON.NET without getting an error?
Got the same issue, I used List<T> instead of T[] to fix it.
You are most likely missing a call to ToObject(...) and a type cast.
This should work:
class Test { public int Value; }
class Program
{
static void Main(string[] args)
{
var array = new Test[2];
var instance = new Test {Value = 123};
array[0] = instance;
array[1] = instance;
var settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.All
};
string serialized = JsonConvert.SerializeObject(array, settings);
// Explicitly call ToObject() and cast to the target type
var deserialized = (Test[]) ((JArray)JsonConvert.DeserializeObject(serialized, settings)).ToObject(typeof(Test[]));
Debug.Assert(deserialized[0].Value == 123);
}
}