When I splice an entry from my one array, it splices the same entry from my other array... What is happening?
private static var words:Array = new WordFile().toString().split(/\n/);
private static var wordsTemp:Array;
public static function checkWord (word:String):Boolean
{
var truefalse:Boolean = wordsTemp.indexOf(word+"\r".toLowerCase()) > -1;
trace (words.length)
wordsTemp.splice(wordsTemp.indexOf(word+"\r".toLowerCase()), 1);
trace (words.length)
return truefalse
}
public static function resetArrays :void
{
wordsTemp = words
}
With this code, I call the resetArrays function every time a new game is started. Once in the game, the program will call checkWord with a word being passed to it. If it is found in the word array, it will splice it from the temporary array. However, when I run it, the 2 traces yield 2 different numbers, with the second one being one lower (assuming the word was found in the array). This seems to me to be very strange as I am splicing the temporary array that gets reset, however when tracing the array that is supposed to be unchanged (there is no operations other than the ones I am showing you with it in it) it seems to be changed by a splice in the temporary array...
Any thoughts?
In AS3, all data types except String and Number (and related) are copied by reference
This means that it only copies a reference to the original object when you use myArray1 = myArray2
In more detail, consider the memory at which words is stored is 0x123456
wordsTemp = words will make wordsTemp point to the same memory, i.e. 0x123456
When you do any operation on words, the array at 0x123456 is modified. But this is what wordsTemp refers to. So in reality both are the same object. To make both different, you need to clone the object. In case of an array, you can clone it using any method that modifies the array and returns the new array, e.g. Array.slice
wordsTemp=words.slice(0, words.length); //will do the trick
Or, you could concatenate nothing to the original array and get a duplicate
wordsTemp=words.concat();
Alternatively, if you want to write more lines of code, here's what you can do:
wordsTemp=new Array();
for (var i:int=0; i<words.length; i++) {
wordsTemp.push(words[i]);
}
When you do wordsTemp = words, you don't just assign the array item, but the memory space in the program. You have to copy all item from the first array to the second, but not set it equal has you do.
You can do a loop to copy all item from words to wordsTemp or do something like :
wordsTemp = words.slice(0, words.length);
It will return a copy of the original array without modifying it
Hop that can help
As far as I know, the easiest and "standard" way to clone an array in AS3 is:
var cloneArray:Array = sourceArray.concat();
Related
var so:SharedObject = SharedObject.getLocal("example");
var arr:Array=[1,2,3];
so.data.arr = arr;
so.flush();
trace(so.data.arr); // outputs 1,2,3
arr[2]=5;
trace(so.data.arr); // outputs 1,2,5
As you see, I updated only arr, but so.data.arr got updated as well.
so.data.arr doesn't update if instead of arr[2]=5; i write arr=[1,2,5];
Seems that arr and so.data.arr are linked somehow, but only if I update an element in the arr, not set the whole arr differently.
I discovered this accidentally. Why is it working like that?
Can I count that it works like that every time and use it? Thanks.
Basically speaking arrays are passed by reference, not by value. That means if you assign an array variable to another one you are not creating a new array.
so.data.arr = arr;
means that both so.data.arr and arr point to the same array object. That's why modifications to either one will be reflected by the other. They are pointing at the same thing. But
arr=[1,2,5];
will make arr point to some other array object, remember that [1,2,5] is a short hand version of new Array(1,2,5).
That's why after that line they aren't "linked" any more.
I have a dictionary of arrays that contain one kind of objects : database records.
Each array is like a table associated to a key (table name) in the dictionary.
My issue is: How do I update a table (delete a record, add a record, etc) without making any temporary copy of the table, like I would do with an NSDictionary of NSArrays ?
Currently, I do :
func addRecord(theRecord: DatabaseRecord, inTable tableName: String)
{
if var table = self.database[tableName]
{
table.append(theRecord)
self.database[tableName] = table
}
else
{
self.database[tableName] = [theRecord];
}
}
The issue is: a temporary copy of the table is made twice. This is pretty inefficient.
Swift arrays use value (copy) semantics but in fact they are passed and assigned as references. Only when a change of the array size or an reordering of the elements occurs the array is really copied. This is called "lazy copy" or "copy on write". You do not have to be scared to simply assign an array like this:
var temp = someBigArray
It is not copied. But when you call:
temp.append(someValue)
Then an actual copy happens.
In your code in this line:
if var table = self.database[tableName]
The array is not copied.
In this line:
table.append(theRecord)
It is copied.
In this line:
self.database[tableName] = table
It is not copied but the ref-count of the array in the dictionary is decremented by one and if no other reference to this array exists it is deallocated. The "table" array is then just passed as a reference into "self.database[tableName]". No copy occurs here.
But Eric D's answer is correct, by calling append() directly on the array (with the ? operator) the least overhead is created. This is how you should do it:
self.database[tableName]?.append(theRecord)
But it can happen also in append() that the array get's copied - which is the case if the array size has to be extended and there is no pre-allocated space. This is because arrays always has to have contiguous memory.
So by calling append() it can happen that the array has to do this:
allocate a new contiguous memory block with the current count size plus at least one entry more. (usually it allocates even more then that, just in case you will append more in the future)
copy the whole content into the new memory block (including the new entry)
release the "old" memory (this is in fact more complicated because other arrays could reference the old memory, in this case it's not released)
So usually you do not have to care about "inefficient" copy of arrays - the compiler knows better.
I think this is a case where you can use optional binding with ? and directly append the value (here with a ternary operator):
func addRecord(theRecord: DatabaseRecord, inTable tableName: String) {
self.database[tableName] == nil ? self.database[tableName] = [theRecord] : self.database[tableName]?.append(theRecord)
}
I am trying to copy a 2D array in a temporary array to a permanent one. The problem is that the array it's trying to copy to is dynamic.
I was looking for a way to get string (the new arrays name) to act as the arrays actual name, not the value of any part of the array. I have looked around and this is roughly what I came to:
(this["dynamicArrayName"]) = tempArray;
or
(this[variableWithName]) = tempArray;
(not even sure thats the best way to copy a 2D array).
However, this doesn't work :(. It comes up with so many errors (not syntax).
If there is some way to turn string into an Arrays/Variables name, it would be fantastic to know about.
Just drop the parens in your first example.
function someFunc():void {
var temp:Array = [1,2,3];
this['someArr'] = temp;
}
trace(this['someArr']);
// will output "undefined"
someFunc();
trace(this['someArr']);
// will output "1,2,3"
I noticed something interesting when using for-in constructs in cfscript: It appears that the variable in struct argument in the for(in) loop is set as an independent variable, and has no reference to its parent array key.
If you run the following code you will see the array doesn't change on output. The variable local.i inside the loop is being assigned the new value, but the array remains unchanged.
function arrayTest()
{
local.foo = ["bar-one","bar-two", "bar-three"];
for (local.i in local.foo)
{
local.i = "I am #local.i#";
// Dump local.i; its output will be 'I am bar-[one[two]] etc...'
}
// Dump local.i; its output will as above
// Dump the array; its keys remain unchanged: "bar-one, bar-two, -bar-three"
writeDump(local.foo);
}
So why is this? I know arrays are passed by reference in CF, but I'm not passing an array here. I'm just using one in a for-in construct. There is a difference, no?
It's a bit misleading to call the argument variable in structure. I see local.i as a shortcut to local.foo[ local.i ]. It sounds like the var is indeed the array key and we can modify it. The solution here is to use a plain for() loop, instead.
I would not expect the underlying array to change unless i was a complex object of some sort or something passed by reference. For example if foo were
local.foo = [{a="b"},{a="c"}];
then modifying local.i.a = "I am key #local.i.a#"; would modify the object within the array, and those changes would be reflected in your dump.
Update:
Ultimately this comes down to pointers or references. In loose terms, local.i is just a pointer to objects within the array. So resetting local.i just points that variable at some other object in memory. It has no impact on the array. Notice the change in hashcode value?
// example
local.foo = [ "bar-one" ];
for (local.i in local.foo)
{
WriteOutput("local.i (before) =#local.i.hashCode()#<br>"); //-335192660
WriteOutput("local.foo[1] = #local.foo[1].hashCode()#<br>");//-335192660
local.i = "I am key #local.i#";
WriteOutput("local.i (after) = #local.i.hashCode()#<br>"); //1075915694
}
writeDump(local.foo);
ActionScript's Array and Vector classes both have a slice() method. If you don't pass any parameters, the new Array or Vector is a duplicate (shallow clone) of the original Vector.
What does it mean to be a "shallow clone"? Specifically, what is the difference between
Array newArray = oldArray.slice();
Vector.<Foo> newVector = oldVector.slice();
and
Array newArray = oldArray;
Vector.<Foo> newVector = oldVector;
? Also, what if the Vector's base type isn't Foo, but something simple and immutable like int?
Update:
What is the result of the following?
var one:Vector.<String> = new Vector.<String>()
one.push("something");
one.push("something else");
var two:Vector.<String> = one.slice();
one.push("and another thing");
two.push("and the last thing");
trace(one); // something, something else, and another thing
trace(two); // something, something else, and the last thing
Thanks! ♥
In your context, what .slice() does is simply to make a copy of your vector, so that newArray refers to a different object from oldArray, except both seem like identical objects. Likewise goes for newVector and oldVector.
The second snippet:
Array newArray = oldArray;
Vector.<Foo> newVector = oldVector;
actually makes newArray a reference to oldArray. That means both variables refer to the same array. Same for newVector and oldVector — both end up referring to the same vector. Think of it as using a rubber stamp to stamp the same seal twice on different pieces of paper: it's the same seal, just represented on two pieces of paper.
On a side note, the term shallow copy differs from deep copy in that shallow is a copy of only the object while deep is a copy of the object and all its properties.
Also, what if the Vector's base type isn't Foo, but something simple and immutable like int?
It's the same, because your variables refer to the Vector objects and not their ints.
What is the result of the following?
Your output is correct:
something, something else, and another thing
something, something else, and the last thing
two = one.slice(), without any arguments, makes a new copy of one with all its current contents and assigns it to two. When you push each third item to one and two, you're appending to distinct Vector objects.