Dart. Cant' change elements of list - arrays

I can't change elements of array using anonymous function:
var collection=[0, 1, 2];
collection.forEach((c)=> c+=1);
print(collection);
This variant doesn't work eithter:
var collection = [0, 1, 2];
for (var x in collection) {
x=x+1;
}
print(collection);

If you wish to replace/update all items in a list (List<int> items = [1, 2, 3]):
items = items.map((x) => x + 1).toList();
for (int i = 0; i < items.length; i++) items[i] += 1;
items = List<int>.generate(items.length, (i) => items[i] + 1);
List<int> tempItems = [];
items.forEach((x) => tempItems.add(x + 1));
items = tempItems;

You can't change the arguments to a function or var-in loop. You can call methods on them, which might have the side effect of mutating them, but assigning directly to them will not alter the value from which they are effectively copied.

collection.forEach((c)=> c+=1);
In above line you are incrementing parameter by 1 and that will not affect to the list.
It's equavalent to:
collection.forEach((int c) {// in this case int because list contains int's
c = c + 1;
});
As you can see, no point of changing the value of a parameter(local variable).
Since it's 0,1,2, you can do like this: collection.forEach((c)=> collection[c] = c+1);. That is stupid way because whenever you change any value, you will get an error(out of range).
So, here are some ways that you can change list values:
The easy way:
void main() {
var collection=[0, 1, 2];
for(int i = 0; i < collection.length; i++) {
collection[i] += 1;
}
print(collection);
}
Using replaceRange:
void main() {
var collection=[0, 1, 2];
var temp = collection.map((c) => c+1).toList();
collection.replaceRange(0, collection.length, temp);
print(collection);
}

Related

String Length changes if it is pulled from file

I'm coding in Game Maker Studio (Toby Fox used it for undertale) and I'm starting to try and work with files. Effectivly, I'm trying to build a custom "level" editor because the built in one for GMS lacks some features I want. So far I have set most of it up, but I still have to make it regurgitate the saved "levels" from the files. It's almost completly working, but there's one problem. Here's some info you should know ahead of time:
GMS uses really nonstandard syntax. I apologize ahead of time for that.
GMS is weird so arrays don't work with JSON formatting
as a result of No. 2, I have coded my own JSON formatting, which I reffer to as GSON (Gamemaker Studio Object Notation)
I am using GSON not only to store levels (and their respective components) but I also want to build a copy paste functionality, which I would use GSON for (copy the component and it just gives you a GSON string which is then interpreted back when you paste it, so that I can copy and paste not only the component type (i.e. a solid vs the player), but the variable values as well).
So, into the meat of it...
global.lineBreak = "\n"
global.sectionSign = "§"
{ // gsonStringifySelf
function gsonStringifySelf(varnamearray, exception = undefined){
/*
"exception" should be set to the variable you will use to store the returned string. if you decide to instead just upload it
directly to the file without a middleman variable, then leave the field empty. e.g.
gsonstring = gsonStringifySelf(varnamearray, "gsonstring")
varnamearray should be an array with the names of all the variables you want to save. I could hardwire it to use
variable_instance_get_names(self)
but this doesn't get instance_variables, so instead its best to have an input, especially so you can leave out
instance variables you don't care about. If you want to put in all the instance variables,you have to mannually add their names to the array, which could be done via the following:
var names = variable_instance_get_names(self) // (var means it is a strictly local variable, so it can't be accessed by any other objects)
var i = array_length(names) // array indexes start at 0 but array_length starts at 1, so I is refrencing the unset index that is closest to index 0
names[i++] = "id" // i++ returns i **then** increments it, causing it to refrence the correct array index, as mentioned above.
names[i++] = "visible"
names[i++] = "solid"
names[i++] = "persistent"
names[i++] = "depth"
names[i++] = "layer"
names[i++] = "alarm"
names[i++] = "toString"
names[i++] = "direction"
names[i++] = "friction"
names[i++] = "gravity"
names[i++] = "gravity_direction"
names[i++] = "hspeed"
names[i++] = "vspeed"
names[i++] = "speed"
names[i++] = "xstart"
names[i++] = "ystart"
names[i++] = "x"
names[i++] = "y"
names[i++] = "xprevious"
names[i++] = "yprevious"
names[i++] = "image_xscale"
names[i++] = "image_yscale"
strvars = gsonStringifySelf(names, strvars)
i++ returns the value of i, then increments it.
The gson formatting works as follows:
first it tells you what kind of object it is:
<objectname>{\n
(\n is the code for "new line")
then it adds the variables, which are formatted as follows:
<type>:<variablename>:<value>\n
then it puts "}\n" on the very end,
all together it looks like this:
<objectname>{\n
<type>:<variablename>:<value>\n
}\n
the types are as follows:
A = Array (list of entries)
B = Boolian (true/false)
R = Real (any and all numbers, yes this is unstandard)
S = String (Letters and Characters)
U = Undefined (Built in Variable "Undefined")
so these two are effectivly equivalent:
number = 20
"R:number:20"
Arrays are a special case:
A:<arrayname>:{,<type>:<value1>,<type>:<value2>...}\n
In this way multi dimentional arrays are instead shown as nested one dimensional arrays.
Multi dimensional arrays and nested arrays respectively look like this:
2 dimensional array that is 2x1 in size
A:<arrayname>:{A:{<type>:<value>},A:{<type>:<value>}}\n
2 length 1 dimensional array, with a 2 length array nested inside
A:<arrayname>:{A:{<type>:<value>,<type>:<value>}, <type>:<value>}\n
I mention this because in a case like this:
a = [
10,
5,
false
]
b = [
a,
"nope"
]
there will be no connection between b[0] and a. Instead, b[0] will be only store the values, and as such changing b[0][1] will not affect the value of a[1]
note that in scenarios like this:
array[0] = 1
array[2] = "hello"
array[1] will also be part of the string, so it will look like this:
A:array:{R:1;R:0;S:hello}\n
This is because of a quirk with GMS. If you set a location in an array "past" an undefined location, then the undefined location is set to 0 when it is accessed.
*/
static lineBreak = "\n"
static sectionSign = "§"
var str = sectionSign + object_get_name(object_index) + "{" + lineBreak // Make the first line = "§<objectname>{\n"
var str = global.sectionSign + object_get_name(object_index) + "{" + global.lineBreak // Make the first line = "§<objectname>{\n"
var names = varnamearray
for(var i = 0; i < array_length(names); i++){
if(names[i] == string(exception)){
continue
}
var tempstr = ""
var r = variable_instance_get(self, names[i])
if(is_array(r)){
tempstr = "A:" + names[i] + ":"
tempstr += string(gsonStringifyArray(r)) + global.lineBreak
} else {
tempstr = gsonValueTypeNotate(r, names[i]) + global.lineBreak
}
str += tempstr
}
str += "}" + global.lineBreak
return str
}
function gsonValueTypeNotate(val, name){
if(name == undefined){
var a = ""
name = ""
} else {
var a = ":"
}
if(is_bool(val)){
var str = "B:" + name + a + string(val)
} else if(is_numeric(val)){
var str = "R:" + name + a + string(val)
} else if(is_string(val)){
var str = "S:" + name + a + val
} else if(is_undefined(val)){
var str = "U:" + name + a + string(val)
}
return str
}
function gsonStringifyArray(array){
var str = "{" + global.sectionSign + ","
var len = array_length(array)
var len1 = len - 1
for(var i = 0; i < len1; i++){
if(is_array(array[i])){
str += "A:" + gsonStringifyArray(array[i]) + global.sectionSign + ","
continue
} else {
str += gsonValueTypeNotate(array[i], undefined) + global.sectionSign + ","
}
}
if(is_array(array[i])){
str += gsonStringifyArray(array[i])
} else {
str += gsonValueTypeNotate(array[i], undefined)
}
str += global.sectionSign + ",}"
return str
}
}
{ // gsonParse
function gsonParseObject(str){
var array
array = stringLineify(str)
var a1 = array[0]
var a2 = string_length(a1)
var a = string_copy(array[0], 2, string_length(array[0]) - 3)
var b = instance_create_depth(0, 0, 0, asset_get_index(a))
var c
for(var i = 1; i < array_length(array) - 1; i++){
if(string_copy(array[i], 1, 1) = "A"){
for(var j = 3; string_copy(array[i], j, 1) != ":"; j++){
}
c[0] = string_copy(array[i], 3, j - 3)
c[1] = parseArrayValue(array[i])
variable_instance_set(b, c[0], c[1])
} else {
c = getVarNameValue(array[i])
if(string_copy(array[i], 1, 1) = "B"){
variable_instance_set(b, c[0], bool(c[1]))
} else if(string_copy(array[i], 1, 1) = "R"){
variable_instance_set(b, c[0], real(c[1]))
} else if(string_copy(array[i], 1, 1) = "S"){
variable_instance_set(b, c[0], c[1])
} else if(string_copy(array[i], 1, 1) = "U"){
variable_instance_set(b, c[0], undefined)
}
}
}
}
function getVarNameValue(str){
for(var i = 3; string_copy(str, i, 1) != ":"; i++){
}
var a
a[0] = string_copy(str, 3, i - 3)
var ind = ++i
for(; string_copy(str, i, 1) != global.lineBreak; i++){
}
a[1] = string_copy(str, ind, i - ind)
return a
}
function parseArrayValue(str){
var array
array[0] = ""
var l
var b = 0
var c = 1
for(var i = 2; i <= string_length(str); i++){
var l = string_copy(str, i, 1)
if(l == global.sectionSign){
i += 2
var l = string_copy(str, i, 1)
if(l == "A"){
c = i + 2
for(var j = c; string_copy(str, j, 1) != "}"; ++j){
}
show_debug_message(string_copy(str, c, ++j))
array[b++] = parseArrayValue(string_copy(str, c, j - c))
i = j - 1
} else if(l == "}"){
break
} else {
for(var j = i; string_copy(str, j, 1) != global.sectionSign and j <= string_length(str); ++j){
var l = string_copy(str, j, 1)
}
array[b++] = getArrValue(string_copy(str, i, j - i))
i = j - 1
}
}
}
return array
}
function getArrValue(str){
var v = string_copy(str, 3, string_length(str))
if(string_copy(str, 1, 1) = "B"){
return bool(v)
} else if(string_copy(str, 1, 1) = "R"){
return real(v)
} else if(string_copy(str, 1, 1) = "S"){
return string(v)
} else if(string_copy(str, 1, 1) = "U"){
return undefined
}
}
}
{ // Save GSON to file
function gsonRoomUnload(file){
global.roomUnload = file
with(all){
event_user(0)
}
event_user(0)
}
function saveString(fname, str){
var b = file_text_open_append(fname)
file_text_write_string(b, str)
file_text_close(b)
}
}
{ // Load GSON from file
function gsonRoomLoad(fname){
if(file_exists(fname)){
var file = file_text_open_read(fname)
var a = ""
var b = ""
for(var i = 0; ; i++){
b = file_text_readln(file)
a += b
if(string_copy(b, 1, 1) == "}"){
//gsonParseObject(a)
a = ""
}
if(string_copy(b, 2, 1) == ""){
break
}
}
return true
} else {
return false
}
}
}
function stringLineify(str){
var strings
strings[0] = ""
var a
a[1] = 0
a[2] = ""
for(var i = 0; a[2] != false; i++){
a = stringLine(str, a[1])
strings[i] = a[0]
}
return strings
}
function stringLine(str, index){
var carriage = "\n"
for(var i = 1; ; i++){
var b = string_copy(str, index + i, 1)
if(b == carriage){
return [string_copy(str, index, i), index + i + 1, true]
} else if(index + i >= string_length(str)){
return [string_copy(str, index, i), index + i + 1, false]
}
}
}
There are a couple of functions you'll notice aren't defined. Those functions are built into GMS. I've linked their documentation at the bottom of the question.
Okay, so here's my problem:
If I run gsonStringifySelf() then take the string it returns and put it in gsonParseObject() then this line:
string_copy(array[0], 2, string_length(array[0]) - 3)
Is correct. Remember, what this does is take this example string:
"§<type>{\n"
and turn it into this:
"<type>"
string_copy(array[0], 2, string_length(array[0]) - 3)
copies from the second location in the string (I will be using "|" to inducate where the code is selecting in the string)
"§|<type>{\n"
and string_length returns the length of the string. so its trying to copy within the "|" in the string
"§|<type>{\n|"
But it is actually trying to copy 1 past the final location of the string, because we skipped the first location of the string but told it to copy the length of the string. Thus, when we tell it how many spaces to copy, we must subtract 3. We subtract 1 so it is only copying up to the final point in the string, and another 2 so that "{\n" isn't included in the final string. (\n is newline, and is considered one character even though it is represented with 2)
so base string:
"§<type>{\n"
copy from second point
"§|<type>{\n"
copy to the end of the string +1
"§|<type>{\n|"
subtract one from how many we are copying to shave the extra space off the end
"§|<type>{\n|"
and then subtract another 2 from how many we are copying to remove "{\n"
"§|<type>|{\n"
and return it:
"<type>"
Here's the thing. If I run gsonStringifySelf() and put it in a file then when I pull it down from the file and put that into gsonParseObject(), instead of this being correct:
string_copy(array[0], 2, string_length(array[0]) - 3)
this is correct:
string_copy(array[0], 2, string_length(array[0]) - 4)
So somehow, in all the file beeswax, an extra character is being added on, and I can't figure out where.
Thanks in advance for the help.
bool(n)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Variable_Functions/bool.htm
real(n)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Strings/real.htm
is_bool(n)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Variable_Functions/is_bool.htm
is_numeric(n)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Variable_Functions/is_numeric.htm
is_string(n)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Variable_Functions/is_string.htm
is_undefined(n)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Variable_Functions/is_string.htm
asset_get_index(str)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Asset_Management/Assets_And_Tags/asset_get_index.htm
string_copy(str, index, count)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Strings/string_copy.htm
string_length(str)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Strings/string_length.htm
with(id)
https://manual.yoyogames.com/GameMaker_Language/GML_Overview/Language_Features/with.htm
event_user(int)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Asset_Management/Objects/Object_Events/event_user.htm
instance_create_depth()
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Asset_Management/Instances/instance_create_depth.htm
variable_instance_get()
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Variable_Functions/variable_instance_get.htm
variable_instance_set()
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Variable_Functions/variable_instance_set.htm
file_exists(fname)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/File_Handling/File_System/file_exists.htm
file_text_open_append(fname)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/File_Handling/Text_Files/file_text_open_append.htm
file_text_write_string(file)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/File_Handling/Text_Files/file_text_write_string.htm
file_text_open_read(fname)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/File_Handling/Text_Files/file_text_open_read.htm
file_text_readln(file)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/File_Handling/Text_Files/file_text_readln.htm
file_text_close(file)
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/File_Handling/Text_Files/file_text_close.htm
Ive tried most everything I can think of. The extra Character is an invisible character, and because of that, itis also invisible when I try to check the value with the debugger. I could bodge it, but if I can i'd rather avoid that, since it'll just cause me problems later anyway.
So after a bit of testing, I've realized that with just how long it takes to load a single instance, it's not worth using. Presumably I would use this for loading the areas during gameplay as well, but it takes well over 10 seconds to load a single instance, and a couple of minutes for more than 4 objects. I'll just have to avoid using arrays. Thanks! (bit annoying that I spent so much time on it for no reason tho...)
GMS is weird so arrays don't work with JSON formatting
The not-so-recently-added json_stringify does. The following
var thing = {
an_int: 1,
a_float: 1.5,
a_bool: true,
a_string: "hi",
an_array: [1, 2, "oh"],
a_struct: { x: 1, y: 2, name: "me", arr: [3, 4, undefined] },
};
show_debug_message(json_stringify(thing));
would output
{ "a_struct": { "x": 1.0, "y": 2.0, "name": "me", "arr": [ 3.0, 4.0, null ] }, "an_int": 1.0, "a_float": 1.5, "a_bool": true, "a_string": "hi", "an_array": [ 1.0, 2.0, "oh" ] }
With help of variable_instance_get_names/variable_struct_get_names, you could assemble a struct with all of the instance's variables (and desired built-in ones), encode that, and upon decoding write them back in.
As for your encoder-decoder, a few implementation caveats plague it:
GameMaker strings are immutable, meaning that adding two strings together generally allocates a new, third one. This makes your encoding slower than it could have been.
Writing to a buffer with kind=buffer_grow is a good way to limit memory re-allocations (as it will double the size whenever it runs out of space).
GameMaker strings are encoded as UTF-8, meaning that string_char_at isn't as simple as adding an index to the string pointer. This makes your decoding slower than it could have been.
Reading from a buffer is a common way around this, but take care - since you'd be storing a UTF-8 string in the buffer, a character will not necessarily be a single byte. Fortunately, you can usually completely ignore this detail so long as you read/write a string as a whole.
You are storing strings verbatim, which probably means that it's going to catch fire the moment one of the strings contains a } or other delimiter.
You are doing loops searching for characters often instead of calling string_pos[_ext] to let the engine do this [quicker].
Although technically recursive, its reliance on grabbing a substring with an encoded value instead of having a read position makes the process of reading nested values far messier than it should be (if I were to guess, it fails after misattributing one of the closing })
But also, you know, if it's your format, who said that it has to be a string? You can work with bytes instead, sparing yourself of a number of problems at once - no need for delimiters when you are writing/reading data in the same order.
A simple buffer-based encoder-decoder fits in just a little over 50 lines of code:
enum BinType { Undefined, Bool, Float, Int, String, Array, Struct };
function buffer_write_value(_buf, _val) {
if (is_real(_val)) {
buffer_write(_buf, buffer_u8, BinType.Float);
buffer_write(_buf, buffer_f64, _val);
} else if (is_bool(_val)) {
buffer_write(_buf, buffer_u8, BinType.Bool);
buffer_write(_buf, buffer_bool, _val);
} else if (is_numeric(_val)) {
buffer_write(_buf, buffer_u8, BinType.Int);
buffer_write(_buf, buffer_u64, _val); // u64 writes signed int64s fine
} else if (is_string(_val)) {
buffer_write(_buf, buffer_u8, BinType.String);
buffer_write(_buf, buffer_string, _val);
} else if (is_struct(_val)) {
buffer_write(_buf, buffer_u8, BinType.Struct);
var _names = variable_struct_get_names(_val);
var _count = array_length(_names);
buffer_write(_buf, buffer_u32, _count);
for (var i = 0; i < _count; i++) {
var _name = _names[i];
buffer_write(_buf, buffer_string, _name);
buffer_write_value(_buf, _val[$ _name]);
}
} else if (is_array(_val)) {
buffer_write(_buf, buffer_u8, BinType.Array);
var _count = array_length(_val);
buffer_write(_buf, buffer_u32, _count);
for (var i = 0; i < _count; i++) buffer_write_value(_buf, _val[i]);
} else buffer_write(_buf, buffer_u8, BinType.Undefined);
}
function buffer_read_value(_buf) {
switch (buffer_read(_buf, buffer_u8)) {
case BinType.Bool: return buffer_read(_buf, buffer_bool);
case BinType.Float: return buffer_read(_buf, buffer_f64);
case BinType.Int: return buffer_read(_buf, buffer_u64);
case BinType.String: return buffer_read(_buf, buffer_string);
case BinType.Array:
var _count = buffer_read(_buf, buffer_u32);
var _arr = array_create(_count);
for (var i = 0; i < _count; i++) _arr[i] = buffer_read_value(_buf);
return _arr;
case BinType.Struct:
var _struct = {};
repeat (buffer_read(_buf, buffer_u32)) {
var _name = buffer_read(_buf, buffer_string);
_struct[$ _name] = buffer_read_value(_buf);
}
return _struct;
default: return undefined;
}
}
and will successfully process the nested struct from the beginning of my answer if you write a value to a buffer, rewind it, and read it back. Add a little logic to detect instances or special cases, and you'll have a solution fit to your specific problems.
Further reading:
Haxe's JsonPrinter is a great example of a recursive JSON encoder.
SNAP is a GameMaker library with a number of encoders-decoders (following the aforementioned buffer principles, often non-recursive) that is both an example of good code and a solution to problems that you might be thinking of having.
Debugger (for stepping through and figuring out where you go wrong)
Profiler (for figuring out what's slow)
YYC (for performance)

AS3 Picking a random object from an Array with certain conditions

I'm looking for the fastest way to pick a random object that has a certain condition (from an array).
In the example below I have a multidimensional array, 50 * 50 that contains objects. I want to pick a random object from that array but that object needs to have a size larger than 100.
while (object.size <= 100)
{
attempts++;
object = grid_array[Math.round(Math.random() * 49)][Math.round(Math.random() * 49)];
}
Currently I have tested this and in some instances it takes over 300+ attempts. Is there a more elegant way to do this?
Thanks,
What I would do is first filter the source array to extract only valid candidates, then return a random one (if there are any).
For example:
function getRandomObject(grid_array:Array, minSize:Number):Object {
var filtered:Array = [];
for(var i:int = 0; i < grid_array.length; i++){
var inner:Array = grid_array[i];
for(var ii:int = 0; ii < inner.length; ii++){
var object:Object = inner[ii];
if(object.size >= minSize){
filtered.push(object);
}
}
}
return filtered.length ? filtered[int(Math.random() * filtered.length)] : null;
}
// example:
var object:Object = getRandomObject(grid_array, 100);
if(object){
// do stuff with `object`
}
I asked if you need the indexes because you could do this with RegExps and the JSON Class (Flash Player 11). With this example I stored the indexes of the objects:
Create random multidimensional Array to test the function
//---I stored a variable size and the indexes inside the Object
//---Size variable will be numbers between 0 and 500
var array:Array = [];
var i;
var j;
var size:uint = 50;
var obj:Object;
for(i = 0; i < size; i++){
array[i] = [];
for(j = 0; j < size; j++){
obj = new Object();
obj.size = Math.floor(Math.random() * 500);
obj.files = i;
obj.columns = j;
array[i][j] = obj;
}
}
Method to get random Object with size property bigger than 100
//---I'll use to search the object a JSON string
var str:String = JSON.stringify(array);
//---Function to get the random Object
function getRandom():Object{
//---RegExp to search object with size between 100 and 500
var reg:RegExp = /\{[^\}]*"size":(?:10[1-9]|1[1-9]\d|[2-5]\d\d)[^\}]*\}/g;
//---Get all matches
var matches:Array = str.match(reg);
//---Return a random match converted to object
//---If no match founded the return will be null
return matches ? JSON.parse( matches[Math.floor(Math.random() * matches.length)] ) : null;
}

how to find the value of an array in a random location?

i have an array of objects that I'm trying to randomize and i was wondering how can i find the data of a random location in an array using Debug.log? this is mainly so i know if it is working right.
my Code:
while(Deck.length != suffledDeck.length)
{
var ranNum = Random.Range(1,Deck.length);
suffledDeck.Add(Deck[ranNum]);
Debug.Log(suffledDeck[ranNum]);
//Debug.Log(suffledDeck[ranNum]);
}
You can shuffle a array with this fonction:
//# http://jsfromhell.com/array/shuffle [v1.0]
function shuffle(o){ //v1.0
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
deck = shuffle(deck);
After that you can acces your array normally.

as3 random array - randomize array - actionscript 3

How do you randomize an array using actionscript 3?
There is a short version using Array.sort() function:
var arr : Array = [0,1,2,3,4,5,6,7,8,9];
function randomize ( a : *, b : * ) : int {
return ( Math.random() > .5 ) ? 1 : -1;
}
trace( arr.sort( randomize ) );
If you don't get "enough" randomness you can sort twice :)
EDIT - explanation line by line:
For Array class method sort() you can pass not only sort options like Array.CASEINSENSITIVE, Array.DESCENDING and so on but also your own custom compare function reference (a callback) that accepts two parameters (two elements from array to compare). From AS3 documentation:
A comparison function should take two arguments to compare. Given the elements A and B, the result of compareFunction can have a negative, 0, or positive value:
A negative return value specifies that A appears before B in the sorted sequence.
A return value of 0 specifies that A and B have the same sort order.
A positive return value specifies that A appears after B in the sorted sequence.
Note: compare function parameters might be typed (if your array is typed) and have any name you want eg.:
function compareElements ( elementA : SomeClass, elementB : SomeClass ) : int;
This method is very useful when you need to sort array elements by their special properties. In randomization case compareFunction randomly returns -1, 0 or 1 and makes array elements to switch their places (indices). I have found that better randomization (in my subjective and mathematically untested opinion) is when method returns only -1 and 1. Also have in mind that sorting function with custom compare function doesn't compare elements sequentially so in some special cases randomization results may differ from what you might expect.
There's a better way that will also allow you to randomize the array in place, if you need that, and it will not make you create more then a single copy of your original array.
package
{
import flash.display.Sprite;
public class RandomizeArrayExample extends Sprite
{
public function RandomizeArrayExample()
{
super();
testDistribution();
}
private function testDistribution():void
{
var hash:Object = { };
var tester:Array = [1, 2, 3, 4];
var key:String;
for (var i:int; i < 1e5; i++)
{
randomize(tester);
key = tester.join("");
if (key in hash) hash[key]++;
else hash[key] = 1;
}
for (var p:String in hash) trace(p, "=>", hash[p]);
}
private function randomize(array:Array):Array
{
var temp:Object;
var tempOffset:int;
for (var i:int = array.length - 1; i >= 0; i--)
{
tempOffset = Math.random() * i;
temp = array[i];
array[i] = array[tempOffset];
array[tempOffset] = temp;
}
return array;
}
}
}
I had an alternative requirement where i wanted to randomly insert lots of source arrays into a target array randomly. Like Rytis i'm a big fan of the forEach, map and sort functions on Arrays.
var randomInsert:Function = function callback(item:*, index:int, array:Vector.<MyItem>):void
{
var j:Number = Math.floor(Math.random() * targetArray.length);
targetArray.splice(j,0,item);
}
targetArray = new Vector.<MyItem>();
sourceArray1.forEach(randomInsert, this);
sourceArray2.forEach(randomInsert, this);
here's an easier function. Works also on multidimensional arrays
function randomizeArray(array:Array):Array
{
var newArray:Array = new Array();
while (array.length > 0)
{
var mn=Math.floor(Math.random()*array.length)
newArray[newArray.length]=array[mn]
array.splice(mn,1)
}
return newArray;
}
I found this very helpful. I hope it can help you too.
// Array to Randomize
var firstArray:Array = ["One","Two","Three","Four","Five","six","seven","eight","nine","ten"];
trace(firstArray); // Prints in order
var newArray:Array = new Array();
function randomizeArray(array:Array):Array
{
var newArray:Array = new Array();
while (array.length > 0)
{
newArray.push(array.splice(Math.floor(Math.random()*array.length), 1));
}
return newArray;
}
var randomArray:Array = randomizeArray(firstArray);
trace(randomArray); // Prints out randomized :)
If you need your array to be shuffled (your elements can not repeat). You could use this function:
/**
* Shuffles array into new array with no repeating elements. Simple swap algorithm is used.
*/
public function shuffleArray(original:Array):Array
{
// How many swaps we will do
// Increase this number for better results (more shuffled array, but slower performance)
const runs:int = original.length * 3;
var shuffled:Array = new Array(original.length);
var i:int;
var a:int;
var b:int;
var temp:Object;
// Copy original array to shuffled
for(i=0; i<shuffled.length; i++){
shuffled[i] = original[i];
}
// Run random swap cycle 'runs' times
for(i=0; i<runs; i++){
// There is a chance that array element will swap with itself,
// and there is always small probability it will make your shuffle
// results not that good, hence try to experiment with
// different runs count as stated above
a = Math.floor(Math.random() * original.length);
b = Math.floor(Math.random() * original.length);
// Swap messages
temp = shuffled[a];
shuffled[a] = shuffled[b];
shuffled[b] = temp;
}
return shuffled;
}
Usage:
var testArray:Array = ["Water", "Fire", "Air", "Earth"];
trace(shuffleArray(testArray).concat());
this is how I randomize my array of 36 cards for a memory game
const QUANT_CARTAS: int = 36;
//get the 36 numbers into the array
for (var i: int = 0; i < QUANT_CARTAS; i++)
{
cartas.push(i);
}
//shuffles them =)
for (var moeda: int = QUANT_CARTAS - 1; moeda > 0; moeda--)
{
var pos: int = Math.floor(Math.random() * moeda);
var carta: int = cartas[moeda];
cartas[moeda] = cartas[pos];
cartas[pos] = carta;
}
// and add them using the random order...
for (i = 0; i < QUANT_CARTAS; i++)
{
var novaCarta: Carta = new Carta();
novaCarta.tipoCarta = cartas[i];
etcetcetc.............
}
choose random string from array
function keyGenerator(len:Number):String
{
function randomRange(minNum:Number, maxNum:Number):Number
{
return (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);
}
var hexArray = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'];
var key = "";
for (var i=0; i<len; i++)
{
key += hexArray[randomRange(0,hexArray.length-1)];
}
return key;
}
usage:
trace(keyGenerator(16));

AS3: Refer to different arrays dynamically?

This might be a simple one, and if it is I'm sure I'll feel stupid.
Basically, I have arrays a1, a2 and a3.
Using a for statement..
for (var i 0; i < 4; i++) {
//stuff
}
How can I push data in the for statement into one of these arrays using i? That is, each time adding the data into the next group up. a[0] -> a[1] etc. I tried getDefinitionByName but it seems that only works for library objects!
Sorry if it's simple!
Cheers in advance.
EDIT:
The code in full where I need this to work. This is what I tried using the first answer I was given.
var fullList:Vector.<Array> = new Vector.<Array>();
for (var i1 = 0; i1 < 3; i1++)
{
fullList.push(new Array());
}
var levelLoader:URLLoader = new URLLoader();
levelLoader.addEventListener(Event.COMPLETE, levelLoaded);
function levelLoaded(e:Event):void
{
var indexcount = 0;
txttileArray = e.target.data.split("a");
txtDebug.text = txttileArray.toString();
for (var i = 1; i < 4; i++)
{
temparray1 = txttileArray[indexcount].split("");
for (var row = 0; row < temparray1.length; row ++)
{
var a = getDefinitionByName(temparray1[row] + "Tile") as Class;
//trace(a);
var b = new a ;
b.x = 17 + 17 * row;
b.y = 17 + 17 * indexcount;
addChild(b);
fullList[indexcount].push(b);
}
indexcount++;
}
}
sorry for the messy variable names.
i have three arrays, a1, a2 and a3. i want to be able access these
arrays based on the value of the variable "i". so for example, if "i"
is 1, i want to push a data value to a1. and so on – hazdog
This is not possible if you have arrays a1, a2 and a3 as local (defined in the function) variables. If they are defined in the class, then here's how you do it:
private var a1:Array=[];
private var a2:Array=[];
private var a3:Array=[];
private function pushValue(identifier:String, value:Object):int {
var name:String="a" + identifier;
var target:Array=this[name] as Array;
if(target == null) {
throw new Error("No such array " + name);
} else {
return target.push(value);
}
}
//And then call it like this:
pushValue("1", "This value goes in array a1");
pushValue("1", "This is another value that goes in array a1");
pushValue("2", "This value goes in array a1");
pushValue("2", "This is another value that goes in array a2");
pushValue("3", "This value goes in array a3");
pushValue("3", "This is another value that goes in array a3");
Otherwise, you can define an array of arrays (or vector of arrays), say holderArray in which you put your target arrays, and then push the value to holderArray[identifier]
EDIT
Instead of defining a1, a2 and a3 in the this object, define it like this:
private var arrayHolder:Object={};
private function init():void {
for(var i:int=1; i<=3; i++) {
arrayHolder["a" + i]=new Array();
}
}
init();
Then, your arrays will be stored as arrayHolder.a1, arrayHolder.a2, arrayHolder.a3 etc
And modify pushValue as follows:
Change
var target:Array=this[name] as Array;
to
var target:Array=arrayHolder[name] as Array;
Inception.
You need to use Array in Array.
private var fullList:Vector.<Array>;
private var total:uint = 4;
function a():void
{
// first creating array list
fullList = new Vector.<Array>();
for (var i:uint = 0; i < total; i++)
{
fullList.push(new Array());
}
}
function b():void
{
// doing the stuff
for (var i:uint = 0; i < total; i++)
{
switch (i)
{
case 0:
fullList[i].push('zero-array-push');
break;
case 1:
fullList[i].push('one-array-push');
break;
case 2:
fullList[i].push('two-array-push');
break;
case 3:
fullList[i].push('three-array-push');
break;
}
}
}

Resources