Variables in object names - actionscript-3

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;
}

Related

Convert an Object's Name to a String

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.

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]

Variable of type asterisk

var test:*;
test = sMC // Some movieClip exported for ActionScript
var f = new test;
Sorry if the question's a bit lame, but I begin to wonder, what does this asterisk, and the snippet mean?
Answering your original question and your question asked in a comment:
An asterisk is a wildcard which means the variable will accept any type of info. Example:
var wildcard:*;
wildcard = "hello";
wildcard = 10;
wildcard = new MovieClip();
All of the above will work.
Variables should be typed as strictly as possible; by this I mean that when you want to assign a MovieClip to a variable, your variable should be typed as a MovieClip. Like so:
var mc:MovieClip = new MovieClip();
This works for anything. If you create your own class, then use that as your type for a variable that holds your class.
var thing:MyClass = new MyClass();
An error will be thrown if you try and assign an unrelated type to a variable, like so:
var thing:MovieClip = "hello";
But as long as your variable type is somewhere along the inheritance chain of what you're assigning to it, then it will work.
var thing:DisplayObject = new MovieClip();
This can be handy if you want to loop through an array containing an assortment of your own classes that extend MovieClip.
var ar:Array = [];
/**
* MyClass extends MovieClip
* MyOtherClass extends MovieClip
*/
ar.push(new MyClass());
ar.push(new MovieClip());
ar.push(new MyOtherClass());
var i:MovieClip;
for each(i in ar)
{
trace(i);
}
Overall the wildcard type is not a recommendation. At worst use Object as everything in flash extends this. One situation where a wildcard or Object can be useful is if you want to create a function that can accept any kind of data. Like so:
var myarray:Array = [];
function addToArray(data:Object):void
{
myarray[myarray.length] = data;
trace(data);
}
OR
function addToArray(data:*):void
{
myarray[myarray.length] = data;
trace(data);
}
Hope this all makes sense.
The asterisk means the variable type is undefined, or a wildcard.
Meaning you can define test as any sort of variable.

AS3 Loading a Class using getDefinition()

I have hit a road block on this and would highly appreciate if someone can help me on this, please. What I am trying to do is to use shared runtime library by loading a swf ('index.swf') which has numerous library objects which are named in sequence such as:
(orange1,orange2,orange3,orange4)
(red1,red2,red3,red4)
I am able to load the swf('index.swf') without any issues and even am able to load the right library asset, but I have to declare the full name as string such as getDefinition('orange1'). What I would like to do is to match first three letters of string and then run a for loop to load up all the classes that match the first three letters. I usually can do this by employing indexOf() method.
here is my code:
public function loadContent():void
{
ldr.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, progressHandler);
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, onloadHandler);
ldr.load(req);
}
public function progressHandler(eProgress:ProgressEvent):void
{
var percent:Number = (eProgress.bytesLoaded / eProgress.bytesTotal);
trace(percent);
}
public function onloadHandler(e:Event):void
{
// THIS IS WHERE I AM TRYING TO MATCH THE STRING
var str:String = "red";
str = (str.indexOf(str));
var ref1:Class = e.currentTarget.applicationDomain.getDefinition(str) as Class
trace(ref1);
}
I would highly appreciate your help.
Thanks.
I think your problem lies in the following lines of code:
str = (str.indexOf(str));
var ref1:Class = e.currentTarget.applicationDomain.getDefinition(str) as Class
indexOf() returns the index of the first occurrence of the specified substring or -1 if the substring doesn't exist. So , you are passing a string representation of some int (either -1 or 0, 1, 2, etc) to getDefinition()... which probably isn't returning a class reference.
Assuming you have some clips named red1, red2, red3, red4 I would do something like the following:
for (var i:int=0; i < 4; i++) {
var classRef:Class = e.currentTarget.applicationDomain.getDefinition("red" + (i+1).toString()) as Class;
trace(classRef);
}

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/