So I'm having trouble figuring out how to compare two arrays for differences and be able to find which elements do not exist in each other. Most examples talk about object lookup for use with a "for each in" loop. That much makes sense, but I have no idea what is going on here:
var item:Sprite;
object_lookup[item] = true;
I'm quite confused because I've never seen anything other than an integer inside of [] such as with arrays.
Object is a dynamic, that means you can assign properties to it at runtime:
var o:Object = {};
o.sayHi = "Hi, it's me";
trace(o.sayHi); //traces: Hi, it's me
o.sayHiWithFunction = function () { return "Hi with function" };
trace(o.sayHiWithFunction()); //traces: Hi with function
If the property you want to assign is not a valid identifier, you have to use the [] and put it as a string, example:
var o:Object = {};
o.this = "Yes, it is this!"; // Error, this is a keyword! Won't compile
o["this"] = "And the correct this!"; //And this one works!
o["more words"] = "More words"; //Works too
Your code is confusing. If object_lookup is an Object instance, you created a property on it called null but you set this property to true. That means that it will have absolutely nothing to do with the Sprite item you declared above it. Which is null, of course, as you didn't assign a sprite object to it and that's why the property name gets evaluated to null. Do not confuse here: the property name "null" is just a string, it has nothing to do with the type null.
Now, the for in loop loops thru all the property names of an Object, and if you know the property names, you can actually look up the values, too. Looks something like this:
var o:Object = {a:"A", b:"B", c:"C"}; // This is equivalent to o.a = "A", o.b = "B", o.c = "C"... which is aquivalent to o["a"] = "A" ;)
for(var i in o) {
trace(i, o[i]) //traces: c C, a A, b B
}
The for each in is a bit different, if you trace i, you will see the values, not the property names as with for in loop.
Otherwise do what Vesper suggested you in the comment, read about Dictionary.
Related
I would like to iterate over an array and if a value exists, I would like to return TRUE.
struct Loops {
var loopStep: LoopStep
}
struct LoopStep {
var template: [Template]
}
struct Template {
var stepType: String
}
let templates: [Template] = [Template(stepType: "FORM_ONE"), Template(stepType: "FORM_TWO")]
let loopStep = LoopStep(template: templates)
let incompleteSteps = [Loops(loopStep: loopStep)]
I have tried this using reduce however cannot make this syntax work
let result = incompleteSteps.reduce(true, $0.loopStep.template.stepType == "FORM_ONE" )
You simply need to use contains(where:) to get a bool return value indicating whether an element matching a closure exists in the collection or not. Since template is an Array itself as well, you actually need to nest two contains(where:) calls if you want to find out if an array of Loops contains any Loops whose template array contains a Template with the matching requirement.
let result = incompleteSteps.contains(where: {$0.loopStep.template.contains(where: {$0.stepType == "FORM_ONE"})})
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.
In Swift 4.0, I have an array of structs. Is there a way to use keyPaths to update all items in the array without using manually iterating like map or forEach? Something similar to objc [people makeObjectsPerformSelector: #selector(setName:) withObject: #"updated"];
struct Person {
var name: String? = "Empty"
}
var people = [Person(), Person()]
//This only updates one person:
people[keyPath: \[Person].[0].name] = "single update"
//I'm looking to accomplish something like this without a map
let updatedPeople = people.map { (person: Person) -> Person in
var copy = person
copy[keyPath: \Person.name] = "updated"
return copy
}
something like
people[keyPath: \[People].all.name] = "update all without manually iterating"
Mutating into a member of an array requires an l-value. Swift's mechanism for l-values is the subscript, so we can use that:
for i in people.indices {
people[i][keyPath: \Person.name] = updated
// or more simply, just:
// people[i].name = "updated"
// This even works too, but I can't see any reason why it would be desirable
// over the other 2 approaches:
// people[keyPath: \[Person].[i].name] = "update"
}
You could also use forEach, but I generally only recommend that over for in cases where you have an existing closure/function to pass in which has type (Index) -> Void:
// meh
people.indices.forEach {
people[$0][keyPath: \Person.name] = "updated"
}
EDIT Responding to your now edited question asking where is the Swift equivalent of [people makeObjectsPerformSelector: #selector(setName:) withObject: #"updated"], the simple answer is that map, which you for some reason reject in your question, is that equivalent. Of course, to do what Objective-C does, we have to use the Objective-C style of object type, namely a class:
class Person {
var name: String? = "Empty"
}
var people = [Person(), Person()]
people = people.map {$0.name = "updated"; return $0} // *
The starred line is how you make the objects in the array perform the "selector".
A struct is a value type, so as you rightly said in your question, we have to insert a temp variable with a var reference:
struct Person {
var name: String? = "Empty"
}
var people = [Person(), Person()]
people = people.map {var p = $0; p.name = "updated"; return p}
[Original answer:]
The use of key paths in your question seems to be a red herring. You're just asking how to set a property of all the structs in an array.
map is just a way of cycling through the array. You cannot magically do this without cycling through the array; if you don't do it explicitly, you have to do it implicitly.
Here's an explicit way of doing it:
struct Person {
var name: String? = "Empty"
}
var people = [Person(), Person()]
let kp = \Person.name
for i in 0..<people.count {
people[i][keyPath:kp] = "updated"
}
That's not actually any more efficient than using map, though, as far as I know; structs are not mutable in place, so we are still filling the array with entirely new Person objects, exactly as we would have done if using map.
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'm attempting to create a list of lists to imitate the functionality of a 2D array in Dart, and I was having trouble for a while figuring out how to make it work.
I originally used List.forEach in order to create the lists and fill them, but after each loop, it's as if I never created the lists. Here's what that code looked like:
currentMap = new List<List<int>>(height);
currentMap.forEach((e) {
e = new List<int>(width);
e.forEach((f) {
f = 1;
});
});
Printing currentMap resulted in a list of null. Here's what I have now:
currentMap = new List<List<int>>(height);
for(int i = 0; i < height; i++) {
currentMap[i] = new List<int>(width);
for(int j = 0; j < width; j++) {
currentMap[i][j] = 1;
}
}
This works exactly as I expected it to.
I understand that this is probably a very basic issue, and I am assuming that forEach does not allow you to modify the element, but I wanted some confirmation on that, and the Dart API docs do not specify except with the phrase "apples the function f to each element..." which may just not be clear to me.
Also, I am aware of things like the List.filled() constructor -- this is just how I am starting to build towards other functionality.
EDIT: Okay, I think I understand now. Arguments are "pass-by-sharing" which means that a copy of a reference to the object is made. This means that you can modify a member of a mutable object that is pointed to by the argument (as follows):
void function(MyObject o) {
o.x = 5;
}
but trying to change what o points to will not change the argument after exiting the function, such as this:
void function(MyObject o) {
MyObject p = new MyObject();
o = p;
}
Dart does not have variable references, all elements are passed as a reference to an object, not to a variable. (In other words, Dart is purely "call-by-sharing" like both Java and JavaScript).
That means that the e parameter to the forEach callback is just a normal local variable, and assigning to it has no effect outside the callback. The same goes for iterators: they return the value in the iterable, but it has no reference back to the iterable after that.
The double loop is what you want when you do "read-modify-write". You read a value from the list, modify it and store it back into the list using the list's index-set operator.
I know you know about List.filled, but just for other readers, I'll give the one-liner to create the two-dimensional array initialized to 1 values:
currentMap = new List.generate(height, (_) => new List.filled(width, 1));
This is because e is only a reference to the List in currentMap not to a currentMap item.
You update a copy of the reference to a List, but this reference has no connection to currentMap.
After the loop, e just gets garbage collected.
This is because of how an Enumerator works and I believe because the Enumerator is immutable You can't modify the collection items when it's being processed in a foreach... removing members and such.
See also:
http://msdn.microsoft.com/en-us/library/ttw7t8t6.aspx
What is the best way to modify a list in a 'foreach' loop?