Convert an Object's Name to a String - actionscript-3

var variable:Object=new Object();
How would you convert "variable" as an object to "variable" as a string? I thought this would work:
var variable:Object=new Object();
var variable_string=String(variable);

You cannot get the name of a variable that holds an instance via said instance.
You could store the instance in an Object against a given key, which could be found using a for...in loop:
var myObject:Object = {};
var objects:Object = { variable: myObject };
for(var i:String in objects)
{
if(objects[i] === myObject)
{
trace(i); // variable
break;
}
}

All class level member names are stored in the string constant pool in your compiled SWF. Thus, it is in fact technically possible to get the name of variable by loading the bytes of the SWF and parsing them (or by using someone else's API to do it). However, this is probably more trouble than it's worth for whatever it is you're doing.

Related

Effective algorithm for finding value objects in collection

Suppose we have some custom object type:
class SomeObjectType {
public var intProperty1:int;
public var intProperty2:int;
public var intProperty3:int;
public var stringProperty1:String;
public var stringProperty2:String;
public var stringProperty3:String;
public var stringPropertyThatActuallyIsInt1:String;
public var stringPropertyThatActuallyIsInt2:String;
public var stringPropertyThatActuallyIsInt3:String;
...
%ABOUT_20_ANOTHER_PROPERTIES_THAT_I_WON'T_USE%
}
We have a collection of more than 20k instances of these objects. And we have just 1 text input that is actually search filter. User can type in this filter field anything he want and if his filter matches with ANY of first 9 fields I described before we should leave this object in collection. Just simple items filtering.
And let me describe how it works in our project now. This algorithm casts all these properties to Strings, concatenate them, and search using indexOf() != -1 method. This is really slow. It takes about 500-900ms on my dev machine and about 3-4s on iPad on every filter change. Horrible statistics, isn't it?
Small note: we use this algorithm in different 3 places in app and objects differs from object I described above but idea is same. I believe that it is a good idea to compare int to int, string to string (implementing some of fast algorithms(there are lots if them)), and convert string that is actually to int and compare them as int to int, but object differs a lot so I need some common algorithm.
If by collection you mean ArrayCollection, I would recommend to use Vector instead.
Vectors are around 50 times faster then ArrayCollections.
If you need databinding, you could have a look at VectorCollection, but I can't imagine the performance to be anywhere close to Vector.
Also if you are not extending class SomeObjectType anywhere, you could gain some performance (especially on iOS) by making it final class SomeObjectType.
You can use dictionary to search, I think it will much faster, you just need to init for one time.
var dict:Dictionary = new Dictionary();
//get properties,in you obj, they are intProperty1,intProperty2 etc,exclude prototype
var properties:Array = ObjectUtil.getClassInfo(SomeObjectType, ["prototype"]).properties as Array;
for each (var obj:SomeObjectType in yourArrayCollection) {
for (var i:int = 0; i < properties.length; i++) {
var qname:Object = properties[i];
var k:String = qname.localName;
var v:String = obj[k];
if (dict[v] == null) {
dict[v] = [];
}
//add only one time
if ((dict[v] as Array).indexOf(obj) == -1) {
(dict[v] as Array).push(obj);
}
}
}
So if you want return all objs contains "5", just return dict[5]

How to pass by reference in ActionScript?

I'm trying to modify an Object in another class. So I have something like this:
MainClass.as:
..
var myObject:Object = new Object();
Class2_Instance.get_JSON(myObject);
trace(myObject.id); // output: undefined. whereas it should be 42. see below.
..
Class2.as
public function get_JSON(url:String , the_object:Object)
{
var request:URLRequest = new URLRequest(url);
var variables:URLLoader = new URLLoader();
variables.dataFormat = URLLoaderDataFormat.TEXT;
variables.addEventListener(Event.COMPLETE, Complete_Handler_JSON(the_object));
try
{
variables.load(request);
}
catch (error:Error)
{
trace("Unable to load URL: " + error);
}
}
function Complete_Handler_JSON(the_object:Object):Function
{
return function(event:Event):void
{
var loader:URLLoader = URLLoader(event.target);
the_object = JSON.parse(loader.data);
trace(the_object.id); //returns 42.
};
}
So the JSON operation performs correctly within Class2, and it assigns the .parse() value to the_object, but I think there is something I don't understand with AS's pass-by-reference logic. Since I was expecting myObject in MainClass.as would change, too.
What should I do to modify the value of the function argument (myObject) directly ?
Thanks !
The issue is in this line in your event handler:
the_object = JSON.parse(loader.data);
The minute you do this, you're no longer dealing with the object which you passed in to the method. You're assigning a new value to the local variable named the_object. It's important to understand that objects are not passed by reference - object references are passed by value. So the_object in your method is a copy of the reference. When you assign a new value, that copy is overwritten with a different reference.
The solution is to pass the reference by reference - this is related to the concept of double pointers in languages such as C++, also known as double indirection. I'm not certain that this is even possible in ActionScript.
A better solution, just return the deserialized object as a return value.
Objects are passed by reference.
But as i see in your code you are overwriting the_object by another one on the line : the_object=JSON.parse(loader.data).
You can create a new variable and then copy the values in the_object:
var json:Object = JSON.parse(loader.data);
for (var k:String in json) {
the_object[k]=json[k];
}

Variables in object names

Is it possible to get the child of an object with variables IN the object instance name?
location_1, location_2 are MCs containing hidden_1, hidden_2 MCs etc.
And I'd ideally like to target the hidden objects with increasing integers inside a for loop. I've removed the for loop for ease of reading in the below:
var i = 0;
var cacheNum = 0;
var locMc = this["location_"+(i+1)]; // This works
var hiddenMc = locMc.this["hidden_"+(cacheNum+1)]; // This doesn't work!
What I'd ideally like to be possible is:
var i = 1;
var cacheNum = 1;
var hiddenMc = location_i.hiddenMc_cacheNum;
Many thanks,
Nick
The syntax for object access by name is the same as array access by number:
object[value];
In the case of arrays, the value will be a number:
var myArray:Array = ["a", "b", "c"];
myArray[0]; // "a"
In case of objects, the value will be a string:
var myObject:Object = {"a": 1, "b": 2, "c": 3}
myObject["a"] // 1
When accessing variables in the current object, you must use this, as in your example. But when using different objects, you just use the bracket notation directly:
var hiddenMc = locMc["hidden_"+(cacheNum+1)];
If the object is a DisplayObjectContainer (like Sprite or MovieClip) and the value you are accessing is a child (as in your case), you can also use the getChildByName method:
var hiddenMc = locMc.getChildByName("hidden_"+(cacheNum+1));
Like any expression, you can string accesses this way:
this["location_"+(i+1)]["hidden_"+(cacheNum+1)]
But as you can see, this is not readable and very error prone. The best way would be to store those hidden MC's in an array and access them from there. It simplifies the code because you don't need to append "hidden_" when accessing them, is usually faster and will never clash with other children with similar names.
You are attempting to bypass encapsulation by allowing code to manipulate the inner workings of an object. Do that at your own peril. Instead, provide some way for the outside object to have access on a limited basis or to be able to ask the object containing those values to perform work upon them on the caller's behalf:
public class Hidden()
{
private var _hiddenValue:int = 5;
public function get hiddenValue():int
{
return _hiddenValue;
}
public function screwWithTheHiddenValue():void
{
_hiddenValue += 25;
}
}
public class Nosey()
{
var hidden:Hidden = new Hidden();
var result:int = hidden.hiddenValue;
// result should be 5
hidden.screwWithTheHiddenValue();
result = hidden.hiddenValue;
// result should now be 30;
}

ActionScript - Get Instance Name From Constructor Without Passing Parameters?

is it possible to obtain the instance name of a class from the class without having to manually pass the instance name as a string parameter to the class constructor?
//Create New SizeClass
var big:SizeClass = new SizeClass();
//-------------
package
{
public class SizeClass
{
public function SizeClass()
{
trace( //-- Instance Name "big" --// );
}
}
}
No, it is not possible to know anything about the containing code block during a constructor, save what you can learn from the stack trace (though that's not available except in the debugger version of Flash). Even if you had a global access point for the containing class, it still would not allow for that access.
Think of a constructor like a method call. In a line of AS, it will be called before the assignment. Eg: var a:Foo = new Foo() the Foo is created (the constructor completes), and then a is populated with whatever just happened. After that point a will remain agnostic of its context (because of encapsulation) unless it is told about it (this is even true on a DisplayObject -- try this( var mc:MovieClip = new MovieClip(); trace( mc.root ) //this will be null ).
I'm keeping this because it is useful albeit not useful to your original answer.
You can always get the name of a class with getQualifiedClassName from the flash.utils package. You can't get a DisplayObject's until well after it has been constructed, but you can simulate this by (I believe) overriding function set name( value:String ):void. If that doesn't work, then try finding it after Event.ADDED and/or Event.ADDED_TO_SAGE.
The instance name isn't very important. You'd better store references of the instances inside an array.
var sizes:Array = new Array();
var big:SizeClass = new SizeClass();
sizes.push( big );
When you want to access them, you can loop through the array.
for (var i:uint = 0; i < list.length; ++i)
{
var size:SizeClass = list[i] as SizeClass;
trace( size );
}
BTW: Instead of an instance name it is possible to add an automatic index to your class.
package
{
public class SizeClass
{
private static var global_index:int = 0;
public const INDEX:int = global_index ++;
}
}
Which you can access like this:
var big:SizeClass = new SizeClass();
trace(big.INDEX) // 0
var small:SizeClass = new SizeClass();
trace(small.INDEX)// 1
source: http://blog.stroep.nl/2010/08/auto-increment-as3-class/

Calculating Dictionary length in Flex

What's the best way to calculate the length of a Dictionary object in Flex?
var d:Dictionary = new Dictionary();
d["a"] = "alpha";
d["b"] = "beta";
I want to check the length which should be 2 for this Dictionary. Is there any way to do it other than looping through the objects?
No, there is no way to check the length of an object(Dictionary is pretty much an object that supports non-String keys) other than looping through the elements.
http://www.flexer.info/2008/07/31/how-to-find-an-objects-length/
You probably don't have to worry about checking if the property is an internal one.
There's a util function in as3corelib which can get the keys in the dictionary. You can check out DicitonaryUtil
The method is:
public static function getKeys(d:Dictionary):Array
{
var a:Array = new Array();
for (var key:Object in d)
{
a.push(key);
}
return a;
}
So you would do getKeys(dictionary).length
You can use associative arrays instead because I don't think it's possible to check the length of a Dictionary object. You could however extend the Dictionary class and add that functionality and override the corresponding methods.
Alternatively, you could loop through it each time to get the length which isn't really a good idea but is available.
var d:Dictionary = new Dictionary();
d["hi"] = "you"
d["a"] = "b"
for (var obj:Object in d) {
trace(obj);
}
// Prints "hi" and "a"
You can also look here for information on using the "setPropertyIsEnumerable" but I believe that's more useful for objects than it is for Dictionary.
You could write a class around a dictionnary that controls insertions/removals so you can keep track of the key count.
Try extending proxy or just do a wrapper.
For anyone stumbling upon this now there is an update to DictionaryUtil.
You can now just call..
var count:int = DictionaryUtil.getKeyCount(myDictionary);
You can get keys of the Dictionary and check the length of keys array as below:
var d:Dictionary = new Dictionary();
d["a"] = "alpha";
d["b"] = "beta";
var count:int = DictionaryUtil.getKeys(d).length;