Can you navigate the contents of a Vector's index via a String? - actionscript-3

Is it possible to do something similar to this in Haxe?
private var _bindingsFiltered:Vector<String>;
_bindingsFiltered = new Vector<String>();
_controller_touched_binding.action = "meta_start";
What I would like to be able to do:
_bindingsFiltered[_controller_touched_binding.action] = "BUTTON_13";
trace(_bindingsFiltered["meta_start"]); //result: "BUTTON_13"
I want to be able to override a specific index too (still accessed via a string), with a new value, rather than keep pushing new content to the end of the vector. I have been using 'openfl.utils.Object' to cheat for now but I am looking for a more reliable approach for the long run.
Is there a way to do this in Haxe?
If not, what are my options?
I would also be interested in a solution for this in AS3, if there is one (avoiding the Array class).
My goal is to find a method that I can use in both languages seamlessly (next-to-none, differences).

Vector's cannot be indexed by string in Haxe. A vector is an array with a fixed size. This is the Haxe manual on that subject.
Instead of vectors, you can use a Map.
class Test {
private var vector:Map<String, String> = new Map<String, String>();
public function new() {
var str = 'haxe';
vector[str] = "is great";
trace(vector[str]);
}
static function main() {
new Test();
}
}
https://try.haxe.org/#F74Ba

I think you could do this using flash.utils.Dictionary:
ActionScript
import flash.utils.Dictionary;
...
var _bindingsFiltered:Dictionary = new Dictionary ();
_bindingsFiltered[_controller_touched_binding.action] = "BUTTON_13";
trace(_bindingsFiltered["meta_start"]); //result: "BUTTON_13"
Haxe
import openfl.utils.Dictionary;
...
var _bindingsFiltered = new Dictionary<String, String> ();
_bindingsFiltered[_controller_touched_binding.action] = "BUTTON_13";
trace(_bindingsFiltered["meta_start"]); //result: "BUTTON_13"

First, do you really want an array / vector / list, or do you really want a hashmap of key / value pairs? How are you using the collection? Why do you want String keys? And related, is this mostly about access symantics (you want to type it this way), or are the runtime reasons you'd want to use strings (serialization / etc)?
From what you've described, it sounds like what you really want is an Object like the ones in AS3/JS/ECMAScript, with square-bracket access symantics -- obj[key]
Yes, you can do that in Haxe. The openfl.utils.Object class is a helper to do exactly this, using Dynamic objects and reflection. It should compile to exactly what you want on all Haxe targets.
In any case, if you'd like to feel like you're not bound to OpenFL, no problem. Copy the openfl/utils/Object.hx file and place it anywhere you like in your project's class path (and update the package statement).
There's nothing particularly OpenFL-ish about that code. It's pure Haxe code with no dependencies. It provides array access with String keys, as well as toString, toLocaleString, propertyIsEnumerable, iterator, isPrototypeOf, and hasOwnProperty functions (which ECMA-folk are used to.)
The transition from AS3/JS to Haxe is a little weird, especially when it comes to dynamic objects, and I've been meaning to blog more about it. ;) Good luck!
ETA: In truth, you probably want to get away from Dynamic/Reflection, and embrace a more type-strict approach. AS3/JS devs don't understand this at first, but it is where the benefits of Haxe come from. If you don't then your Haxe experience is likely to be unplesant.

Short answer: yes, you can.
abstract MyVector<T>(Vector<T>) {
public function new(l:Int) this = new Vector<T>(l);
#:op([]) public function set<K:T>(s:String, v:K) {
switch (s) {
case "FIRST": this[0] = v;
case "SECOND": this[1] = v;
default: return;
}
}
#:op([]) public function get(s:String) {
switch (s) {
case "FIRST": return this[0];
case "SECOND": return this[1];
default: return cast 0;
}
}
}
var mv = new MyVector<String>(2);
mv["SECOND"] = "Second";
trace(mv["SECOND"]); // outputs Second
You can inline get and set methods if you want.

Related

Using object accessors in AS3

I was looking at a javascript code here:
https://www.w3schools.com/js/js_object_accessors.asp
Where they have shown using object accessors in javascript.
Being a superset of JS, I tried to use it in AS3 but I am getting compiler error
var p = {
get a() {
return 1;
}
};
trace(p.a);
I hope there must be some way to use it in AS3? Any ideas?
I already know about the longer syntax though which of course works:
var p = {
a:function() {
return 1;
}
};
trace(p.a());
AS3 syntax is very different from JavaScript. Variables have type (even if it's just Object). The AS3 reference shows an example of creating a dynamic object, which is really more of an associative array:
var obj:Object = {a:"foo", b:"bar"};
While AS3 does allow for some of the idiosyncracies of JavaScript, I strongly suggest that you treat it like a proper strongly-types programming language (more like C# or Java, than JavaScript) because this leads to cleaner code and better performance (in some cases).
If you know the properties of the object at design time, you should create classes with get/set properties and methods, because these will have much better performance than a dynamic object.
Adding dynamic functions to objects relies on using the Function type.
Yes there are getters and setters in AS3, but they are available in classes only.
Implementation:
package
{
public class ExampleGS
{
public function get a():int
{
return 1;
}
}
}
Usage:
var p:* = new ExampleGS;
trace(p.a); // 1
More info about getters and setters.

AS3: How to Deep Copy an Object

i have an object that i need to copy to my SharedObject data.
The problem is that the data property of shared object is read-only, so i can't clone my 'source' object and assign it to the shared object data, i have to make a copy of them in this way:
var so: SharedObject = SharedObject.getLocal("appData");
copyObject(sourceObj, so.data);
so.flush();
and the copy method:
public static function copyObject(sourceObject:Object, destinationObject:Object):void{
// this would be the code that i need
}
Also have in mind that my object has properties that are objects, so it has inside n leves of objects. That is why i can't simply make a for each and assign all properties on the first level, so what i need is to make a DEEP copy, probably recursive. I tried for hours to make this copyObject method with no success. Also i've searched on the internet but i didn't find any object copy that suits me.
Can someone please help me with this method? I would really apreciate it!
Thank you for your help!
The solution is to write your object to a byte array, encoded it to a string(optional - you can probably save the byte array as well, haven't looked it up) and save it to your shared object.
This function will take an object and turn it into a string
public static function serializeToString(value:Object):String{
if(value==null){
throw new Error("null isn't a legal serialization candidate");
}
var bytes:ByteArray = new ByteArray();
bytes.writeObject(value);
bytes.position = 0;
return Base64.encodeByteArray(bytes);
}
This one will get your object back from a string.
public static function readObjectFromStringBytes(value:String):Object{
var result:ByteArray = Base64.decodeToByteArray( value) as ByteArray;
result.position = 0;
return result.readObject();
}
The Base 64 encoding class you can find here https://github.com/juancgarcia/screenshotify/blob/master/Downloadify-652377f/src/com/dynamicflash/util/Base64.as.
You need to implement IExternalizable on all objects you want to store this way. The implementation includes making writeExternal method called against a ByteArray when you do writeObject(), and readExternal methods, that's called against a newly created instance, so your class should write the necessary metadata in order to make your object deep-cloned, including writing property objects.
Manual on IExternalizable
And on a side note, you should not store one object in the entire so.data, you'd better assign a field in so.data and stuff your object copy in there.
For complex objects I would use RegisterClassAlias:
import flash.net.registerClassAlias;
registerClassAlias("YourClassName", YourClassName);
var so:SharedObject = SharedObject.getLocal("objectName");
so.data.yourData = YourClassName.instance;
so.flush();
For simple Object type with deep level of simple data (primitives including arrays) I would simply use JSON.stringify() and JSON.parse() when reading back the data.

AS3 - Returning a property of a class rather than the class itself

In ActionScript 3, there are some classes that will represent a value rather than the class itself. It's hard to explain properly what I mean, so take this example:
var str:String = "something";
var mc:MovieClip = new MovieClip();
trace(str); // something
trace(mc); // [object MovieClip]
You'll notice that the first trace outputs a value, rather than [object String]. Ontop of this, I can still make use of methods of String, like this:
var ar:Array = str.split('s');
Even though in a way you could almost read the above as:
"something".split('s');
I have a class AvLevelData that has some methods that deal with level data (which is essentially a String). At the moment there is a property data:String which represents the core level data.
The question I have is - can I replicate the behaviour of String in that when I trace or assign an instance of AvLevelData, the result is actually the String data.
For example, at the moment I need to go:
var levelData:AvLevelData = new AvLevelData();
trace(levelData.data);
To get the data. I instead want to be able to simply do the following:
var levelData:AvLevelData = new AvLevelData();
trace(levelData); // some level data string
Is this possible?
If you wan't your object to trace out your own fabricated string then you must implement a toString() function on your AvLevelData class.
In your example above, the MovieClip trace outputs: [Object MovieClip]; this comes from the default toString() implementation for Object (found on Object.prototype) . Note, you cannot override toString() as it only exists on the prototype of Object (remnants of the AS2/Javascript world), all you need to do is provide your own implementation with the same name. For instance:
public function toString():String {
return "MyCustomObjectString";
}
Some of the most basic types - String, int, Number, uint, Boolean, to name a few - are not classes / objects per se, they are primitives. In some languages there is a wrapper class available for some of these so they can be treated like objects, though Flash doesn't do this so much from my experience.
Probably the best way to answer your question is to make a toString() method for your AvLevelData class:
public function toString():String {
return data;
}
Any time you treat a class as a string (such as by putting it in trace()), flash (and many other languages) try to call toString() on the object. Typically this results in a string that's not helpful. But if you define your own toString() method, you can control what string gets output.
Another option is to simply do:
trace(AvLevelData.data);
Since that variable is a string, it should trace just fine.

Creating a "true" HashMap implementation with Object Equality in ActionScript 3

I've been spending some of my spare time working a set of collections for ActionScript 3 but I've hit a pretty serious roadblock thanks for the way ActionScript 3 handles equality checks inside Dictionary Objects.
When you compare a key in a dictionary, ActionScript uses the === operator to perform the comparison, this has a bit of a nasty side effect whereby only references to the same instance will resolve true and not objects of equality. Here's what I mean:
const jonny1 : Person = new Person("jonny", 26);
const jonny2 : Person = new Person("jonny", 26);
const table : Dictionary = new Dictionary();
table[jonny1] = "That's me";
trace(table[jonny1]) // traces: "That's me"
trace(table[jonny2]) // traces: undefined.
The way I am attempting to combat this is to provide an Equalizer interface which looks like this:
public interface Equalizer
{
function equals(object : Object) : Boolean;
}
This allows to to perform an instanceOf-esq. check whenever I need to perform an equality operation inside my collections (falling back on the === operator when the object doesn't implement Equalizer); however, this doesn't get around the fact that my underlying datastructure (the Dictionary Object) has no knowledge of this.
The way I am currently working around the issue is by iterating through all the keys in the dictionary and performing the equality check whenever I perform a containsKey() or get() operation - however, this pretty much defeats the entire point of a hashmap (cheap lookup operations).
If I am unable to continue using a Dictionary instance as the backing for map, how would I go about creating the hashes for unique object instances passed in as keys so I can still maintain equality?
How about you compute a hash code for your objects when you insert them, and then look them up by the hash code in your backing dictionary? The hashcode should compare === just fine. Of course, that would require you to have a Hashable interface for your object types instead of your Equalizer interface, so it isn't much less work than you are already doing, but you do get the cheap lookups.
How about rather doing this:
public interface Hashable {
function hash():String;
}
personally, I ask myself, why you want to do this ... hashing objects to obtain keys makes little sense if they are mutable ...
also, you might consider using a different approach, as for example this factory:
package {
public class Person {
/**
* don't use this!
* #private
*/
public function Person(name:String, age:int) {
if (!instantiationAllowed)
throw new Error("use Person.getPerson instead of constructor");
//...
}
private static var instantiationAllowed:Boolean = false;
private static var map:Object = {};
private static function create(name:String, age:int):Person {
instantiationAllowed = true;
var ret:Person = new Person(name, age);
instantiationAllowed = false;
}
public static function getPerson(name:String, age:int):Person {
var ageMap:Array = map[name];
if (ageMap == null) {
map[name] = ageMap = [];
return ageMap[age] = Person.create(name, age);
}
if (ageMap.hasOwnProperty(age))
return ageMap[age];
return ageMap[age] = Person.create(name, age);
}
}
}
it ensures, there's only one person with a given name and age (if that makes any sense) ...
Old thread I know, but still worth posting.
const jonny1 : Person = new Person("jonny", 26); const jonny2 : Person = new Person("jonny", 26);
is creating two completely different objects that will not compare using ==, guess I don't see why it's any more of a road block because of as3
The problem with AS3/JavaScript/EcmaScript is not that they create two different, equivalent objects.
The problem is that they cannot equate those two equivalent objects--only identity works, since there is no equals or hashCode methods that can be overriden with class-specific comparison logic.
For Map implementations such as dynamic Object or Dictionary, this means that you have to either use Strings or references as keys: you cannot recover objects from a map using different but equivalent objects.
To work around that problem, people either resort to strict toString implementations (for Object maps) which is undesirable, or to instance control for Dictionaries, as in #back2dos example, which introduces different problems (Also, note that #back2dos solution does not really guarantee unique Person instances since there is a time window during which asynchronous threads will be allowed to instantiate new Persons).
#A.Levy's solution is good except that in general, hashCodes are not strictly required to issue unique values (they are meant to map entries to buckets allowing for fast lookups, wherein fine-grained differentiation is done through equals method).
You need both a hashCode and an equals method, e.g.
public interface IEquable
{
function equals(object : Object) : Boolean;
function hash():String;
}
In any programming language,
const jonny1 : Person = new Person("jonny", 26);
const jonny2 : Person = new Person("jonny", 26);
is creating two completely different objects that will not compare using ==, guess I don't see why it's any more of a road block because of as3

What's the cleanest way to simulate pass-by-reference in Actionscript 3.0?

Actionscript 3.0 (and I assume Javascript and ECMAScript in general) lacks pass-by-reference for native types like ints. As a result I'm finding getting values back from a function really clunky. What's the normal pattern to work around this?
For example, is there a clean way to implement swap( intA, intB ) in Actionscript?
I Believe the best you can do is pass a container object as an argument to a function and change the values of some properties in that object:
function swapAB(aValuesContainer:Object):void
{
if (!(aValuesContainer.hasOwnProperty("a") && aValuesContainer.hasOwnProperty("b")))
throw new ArgumentError("aValuesContainer must have properties a and b");
var tempValue:int = aValuesContainer["a"];
aValuesContainer["a"] = aValuesContainer["b"];
aValuesContainer["b"] = tempValue;
}
var ints:Object = {a:13, b:25};
swapAB(ints);
I suppose an alternative would be somewhere defining this sort of thing ...
public class Reference {
public var value:*;
}
Then use functions that take some number of Reference arguments to act as "pointers" if you're really just looking for "out" parameters and either initialize them on the way in or not and your swap would become:
function swap(Reference a, Reference b) {
var tmp:* = a.value;
a.value = b.value;
b.value = tmp;
}
And you could always go nuts and define specific IntReference, StringReference, etc.
This is nitpicking, but int, String, Number and the others are passed by reference, it's just that they are immutable. Of course, the effect is the same as if they were passed by value.
You could also use a wrapper instead of int:
public class Integer
{
public var value:int;
public function Integer(value:int)
{
this.value = value;
}
}
Of course, this would be more useful if you could use operator overloading...
Just look at some Java code. Java has had the convention that reference types are passed by reference and primitive types are passed by value since it's inception. It's a very good model in many ways.
But talking about swap, the best and easiest way to do a swap in Java/AS3 is with the following three lines:
var temp:int = array[i];
array[j] = array[i];
array[i] = temp;
Theres not really any reason to use a function to do a simple swap, when you can do it faster with just 3 lines.
It is annoying. But if you use different idioms than in e.g. C#, you can get reasonable-quality results. If you need to pass a lot of parameters back and forth, pass in an object filled with the needed data, and change the object's parameters when you return. The Object class is for just this sort of thing.
If you just need to return a bunch of data, return an Object. This is more in keeping with the ECMAScript style than pass-by-ref semantics.
Destructuring assignment (e.g. [a,b] = [b,a]) isn't defined in the ECMA-262 3 specification, and it's not implemented in JavaScript 1.5, which is the version equivalent to the JScript implementation in IE. I've seen this syntax in the AS4 specifications preview though, and I believe it's part of JavaScript 1.7.
If ActionScript works like Javascript,
[a,b] = [b,a]