Sorting an array by a private variable - actionscript-3

So... I have spent the last hour trying to figure out why the sort method for my array was not working properly, when I realized that the variable I was trying to sort by in my objects was not publicly available. I have accessed it by using a getter method, which has worked fine for all other purposes. My questions is: Is it possible to sort by a private variable somehow? Perhaps by using a getter method, but I don't know how that would work syntactically. Or do I just have to make my variable public?
On a slightly related note, is there some way to sort on a variable of an object in a vector using standard methods?

Here is an example using get function to sort
var k:Vector.<A> = new Vector.<A>();
k.push(new A(5));
k.push(new A(3));
k.push(new A(7));
k.push(new A(1));
k.push(new A(9));
k.sort(compareFunction);
private function compareFunction(obj1:Object, obj2:Object, properties:Array = null):int
{
var a1:A = obj1 as A;
var a2:A = obj2 as A;
if (a1.level > a2.level)
{
return 1;
}
else if (a1.level < a2.level)
{
return -1;
}
else
{
return 0;
}
}
class A {
private var _level:int;
public function get level():int
{
return _level;
}
public function A($level:int)
{
_level = $level
}
}

Related

Nonfunctioning "for loop" for addressing each variable of class

I'm learning AS3 but have some antiquated background in programming (TP and Atari Basic). On this forum I learned to use a loop such as the one below to address each variable in an object class, in order to make a clone of the object (deep or shallow) or in my case to build the text for a tooltip. However mine doesn't work. Here's the loop, following is an explanation, any help you can give I'd appreciate greatly!
var tooltipText:String;
var i:String;
for (i in bsm) {
if (!(bsm[i] is String)) {
if (bsm[i] != 0) {
tooltipText = i + ": " + bsm[i];
tooltip.extendTooltip(tooltipText, 0xFFFFFF);
}
}
}
Please forgive the horrible variable names. 'i' is a String. 'bsm' is a non-null instance of class StatMod, which begins with
public class StatMod extends Object {
public static const ENCHANTMENTMODIFIER:String = "enchantmentModifier";
public var enchantmentType:String = "None";
public var enchantmentDescriptor:String = "None";
public var minDamage:Number = 0;
public var maxDamage:Number = 0;
public var attackSpeed:Number = 0.2;
The intended behavior is to go through each of the variables of StatMod (I'm not showing them all and I will add more later), and if the variable is a non-zero number, make a string ("attackSpeed: 0.2" for instance) and then add that string to the tooltip. The tooltip.extendTooltip function is working properly.
The observed behavior is basically the computer believing that there are no variables in bsm.
What can I say or do to convince the computer that there actually are variabels in bsm?
The behavior you're expecting is only the case when iterating over dynamically attached properties. For example, if you marked your class dynamic:
public dynamic class StatMod { }
Then added some values to it at runtime:
bsm.test = 5;
Your loop will find the property test with the value 5.
Some options you have to achieve what you want are:
Extend the Proxy class to define what properties are iterable via nextName and nextNameIndex.
Use describeType to generate a list of all the public properties.
Though a simpler method is to expose a list of the properties you want to iterate over and use that in your loop instead, something like:
public class StatMod {
// Existing properties etc.
private _properties:Vector.<String>;
public function get properties():Vector.<String> {
if (_properties === null) {
_properties = new <String>[
'enchantmentType',
'enchantmentDescription',
'minDamage',
'maxDamage',
'attackSpeed'
];
}
return _properties;
}
}
Then:
for (var i:int = 0; i < bsm.properties.length; i++) {
var prop:String = bsm.properties[i];
trace(prop, bsm[prop]);
}

Unity3D: How to access List<T> elements from ParseObject subclass

I cannot seem to access an array of custom objects (that is a column in a Parse table) after querying for it and receiving the results.
I have a simple custom class call "TextEntry" that contains 2 strings.
public class TextEntry
{
public string key;
public string text;
public TextEntry() { }
}
I have a ParseObject subclass called "LocalePO", which has an IList member in addition to other native types.
[ParseClassName("LocalePO")]
public class LocalePO : ParseObject
{
[ParseFieldName("version")]
public int version
{
get { return GetProperty<int>("version"); }
set { SetProperty<int>(value, "version"); }
}
[ParseFieldName("code")]
public string code
{
get { return GetProperty<string>("code"); }
set { SetProperty<string>(value, "code"); }
}
[ParseFieldName("name")]
public string name
{
get { return GetProperty<string>("name"); }
set { SetProperty<string>(value, "name"); }
}
[ParseFieldName("keypair")]
public IList<object> keypair
{
get { return GetProperty<IList<object>>("keypair"); }
set { SetProperty<IList<object>>(value, "keypair"); }
}
public LocalePO() { }
}
I can query to Parse and successfully return a LocalePO object, but I cannot access the specific "TextEntry" members of the "keypair" List afterwards.
var cloudQuery = new ParseQuery<LocalePO>();
var queryTask = cloudQuery.FirstAsync();
// wait for query to return
while (!queryTask.IsCompleted) yield return null;
LocalePO locale = queryTask.Result;
int CloudVersion = locale.version; // this works
List<TextEntry> list = new List<TextEntry>();
list = locale.keypair.Cast<TextEntry>.ToList(); // this doesn't work
foreach (var item in locale.keypair)
{
var entry = item as TextEntry; // this does not work
TextEntry entry = (TextEntry)item; // this doesn't work either
// this is my current solution which works but seems terrible
string json = JsonConvert.SerializeObject(item);
TextEntry entry = JsonConvert.DeserializeObject<TextEntry>(json);
list.Add(entry);
}
I feel like I am overlooking something very simple here, but I just want to convert the data I pull from Parse to local objects so I can use the data throughout the app logic.
It seems to me that Parse prefers the IList of type "object"vs an IList of type "TextEntry" type for the ParseFieldName. For example, Parse always returns null for the field if I have the following:
[ParseFieldName("keypair")]
public IList<TextEntry> keypair
{
get { return GetProperty<IList<TextEntry>>("keypair"); }
set { SetProperty<IList<TextEntry>>(value, "keypair"); }
}
Perhaps I should derive TextEntry from ParseObject too? I'm so confused.
Any help would be appreciated.
Thanks!
Try this:
var cloudQuery = new ParseQuery<LocalePO>();
cloudQuery .Include("keypair");
var queryTask = cloudQuery.FirstAsync();
TextEntry will need to derive from parseObject and you will need to register it as a subclass.
Here is an example of a query I am using which has 2 levels of nested iList's of parseObject subclasses
ParseObject.RegisterSubclass<ProgramDataParse>();
ParseObject.RegisterSubclass<WorkoutDataParse>();
ParseObject.RegisterSubclass<ExerciseDataParse>();
var programQuery = new ParseQuery<ProgramDataParse>()
.OrderByDescending("createdAt").Limit(2)
.Include("workouts")
.Include("workouts.exercises");

How to instantiate an array of custom classes in Action Script 3.0

I'm new to AS3 and I'm getting this error while trying to implement OO style code.
Incorrect number of arguments. Expected no more than 0.
When I try to:
var countries:Country = new Country(10);
Normally this would work in Java or C++, so I'm not sure what's up!?
Here is my custom class.
package {
public class Country {
var cName:String = "noName";
public function Country() {
// constructor code
}
public function setName(n:String):void {
cName = n;
}
public function getName():String {
return cName;
}
}
}
You are passing 10 to the constructor, which is not what you want to do. To instantiate an array of instances, try something like this:
var countries:Array = []
var country:Country;
for (var i:uint = 0; i < 10; i++) {
country = new Country()
country.setName("Country_" + i);
countries.push(country)
}
your constructor function public function Country() {} not have an argument, but you give 10, must go wrong.
ActionScript's array not like c++, don't need element type <Country>
you want to save class in array is simple: var arr:Array = [new Country()]

Get all static variables in a class

I have this ObjectType class which is a class to help me do something like this:
object.type = ObjectType.TWO
//ObjectType.as
package
{
public class ObjectType
{
public static var ONE:String = "one";
public static var TWO:String = "two";
public static var THREE:String = "three";
public function ObjectType()
{
}
}
}
Let's suppose I'm creating a new class and I need a property named type. In that property set function I want to make sure that it's value is one of the ObjectType variables. How can I achieve this?
public function set type(value:String):void
{
for (var o:Object in ObjectType) {
if (value == o)
this._type = value;
} else {
//error
}
}
}
Not performance aware but without modifying anything you can use describeType function to check the static field and get the value back:
function valueInClass(clazz:Class, value:*):Boolean {
return describeType(clazz).variable.(clazz[#name.toString()] == value).length() != 0
}
public function set type(value:String):void
{
if (valueInClass(ObjectType, value)) {
this._type = value;
} else {
//error
}
}
I suppose the second code example you presented doesn't work...
I think it is because you're using the for in loop a little bit wrong.
for (var blah:String in somewhere){
// blah represents a KEY of the somewhere object
// to get the value of this key, use:
var theValue = somewhere[blah];
}
It's the for each loop that loops through the values. But for now I'll use the for in.
Also, it's not in ObjectType, but rather in the class' prototype, that is in ObjectType.prototype.
So, to fix this:
for (var o:* in ObjectType.prototype) {
if (value == ObjectType.prototype[o])
this._type = value;
} else {
//error
}
}
You can solve this using reflection.
A similar question was asked just a few days ago, you should be able to use the same solution, found here.
It should be noted that while the the accepted answer is right, it's also really slow. Not something that you want to do a lot. There are three simpler solutions.
One: Check the value itself:
public function set type(value:String):void
{
if( value != ObjectType.ONE && value != ObjectType.TWO && value != ObjectType.THREE )
return;
}
Obviously, the more constants you have the check the harder this becomes.
Two: Use ints as your constants
Change your ObjectType class to use ints:
public class ObjectType
{
public static var NONE:int = 0;
public static var ONE:int = 1;
public static var TWO:int = 2;
public static var THREE:int = 3;
public static var TOTAL:int = 4;
}
Notice the NONE and TOTAL in there? This makes it easy to check if your value is in the right range:
public function set type(value:int):void
{
if( value <= ObjectType.NONE || value >= ObjectType.TOTAL )
return;
}
You can add more values as needed and you just need to update TOTAL and it'll still work. This needs each value to be in order though.
Three: Use Enums
While Flash has no in-build class for enums, there's a lot of solutions available. Check our the Enum class from Scott Bilas: http://scottbilas.com/blog/ultimate-as3-fake-enums/
Using this as your base class your ObjectType class becomes:
public final class ObjectType extends Enum
{
{ initEnum( ObjectType ); } // static ctor
public static const ONE:ObjectType = new ObjectType;
public static const TWO:ObjectType = new ObjectType;
public static const THREE:ObjectType = new ObjectType;
}
And your check now becomes:
public function set type(value:ObjectType):void
{
...
}
Here, your setter now becomes type safe and will throw errors if anything other than an ObjectType is used.
It turns out that if using an ENUM type of check you should check for the constants property, not variables as showin in the example here:
ActionScript - Determine If Value is Class Constant

Trying to understand the AsyncToken in Flex/Actionscript

I am trying to understand the way the AsyncToken works in actionscript. How can I call a remote service and ensure that a specific parameter is available in the result or fault event functions? I think it is the async functionality I want to use.
The following code will hopefully explain what I am trying to do. Feel free to modify the code block as your explanation.
Thanks.
public function testSerivceCall(data:Object, callBackCommand:String):void
{
// Assume callBackCommand == "FOO";
// How can I pass in callBackCommand as a parameter to the result or fault events?
// How do I create an async token here?
var remoteObject:RemoteObject;
remoteObject = new RemoteObject();
remoteObject.destination = "zend";
remoteObject.source = "MyService";
remoteObject.endpoint = "http://example.com/service";
remoteObject.test.addEventListener(ResultEvent.RESULT, _handleTestResult);
remoteObject.test.addEventListener(FaultEvent.FAULT, _handleTestFault);
remoteObject.test(data);
}
private function _handleTestResult( event:ResultEvent ) : void
{
// How do I get the async token value?
// How can I get the value of callBackCommand in this code block?
if (callBackCommand == "FOO")
{
// do something related to "FOO"
}
else
{
// do something else with the result event
}
}
private function _handleTestFault( event:FaultEvent ) : void
{
// How do I get the async token value?
// How can I get the value of callBackCommand in this code block?
}
An edit to make this question more clear:
Assume I make the following method call somewhere in my code:
testSerivceCall(personObject, "LoginCommand");
How do I get access to the actual string "LoginCommand" inside the _handleTestResult function block?
The reason I want to do this is because I want to dynamically call back certain functions and hand off the result data to specific commands that I know ahead of time when I am making the service call.
I am just having a time grokking the AsyncToken syntax and functionality.
I did not even need closures. I added a class as below which I called externally.
The call was like this:
public class MyClass
{
...
var adminServerRO:AdminServerRO = new AdminServerRO();
adminServerRO.testSerivceCall("FOO",cptyId);
}
public class AdminServerRO
{
private function extResult( event:ResultEvent, token:Object ) : void
{
//the token is now accessed from the paremeter
var tmp:String = "in here";
}
private function extFault( event:FaultEvent ) : void
{
var tmp:String = "in here";
}
public function testSerivceCall(callBackCommand:String, cptyId:String):void
{
var remoteObject:RemoteObject = new RemoteObject();
remoteObject.destination = "adminServer";
var token:AsyncToken = remoteObject.getCounterpartyLimitMonitorItemNode(cptyId);
token.addResponder(new AsyncResponder(extResult,extFault,cptyId));
}
}
While the accepted answer will accomplish what the original submitter wants it does not actually answer the question which was asked. An AsyncToken is created as a result of a remote method call and is accessible from the ResultEvent. Since AsyncToken is a dynamic class you can add whatever property to it that you want. The code below should demonstrate this:
public function testSerivceCall(data:Object, callBackCommand:String):void
{
var remoteObject:RemoteObject;
remoteObject = new RemoteObject();
remoteObject.destination = "zend";
remoteObject.source = "MyService";
remoteObject.endpoint = "http://example.com/service";
remoteObject.test.addEventListener(ResultEvent.RESULT, _handleTestResult);
remoteObject.test.addEventListener(FaultEvent.FAULT, _handleTestFault);
var token:AsyncToken = remoteObject.test(data);
token.callBackCommand = callBackCommand;
}
private function _handleTestResult( event:ResultEvent ) : void
{
if (event.token.callBackCommand == "FOO")
{
// do something related to "FOO"
}
else
{
// do something else with the result event
}
}
private function _handleTestFault( event:FaultEvent ) : void
{
//event.token.callBackCommand should be populated here too
}
If you want to access the properties used during the remote call (parameters to the call and/or AsycToken), you can make use of closures. Just define the result event handler inside the calling method as a closure. It can then access any variable in the calling function.
public function testSerivceCall(data:Object, callBackCommand:String):void
{
var _handleTestResult:Function = function( event:ResultEvent ) : void
{
// token is visible here now
if (callBackCommand == "FOO")
{
// do something related to "FOO"
}
else
{
// do something else with the result event
}
}
var remoteObject:RemoteObject;
remoteObject = new RemoteObject();
remoteObject.destination = "zend";
remoteObject.source = "MyService";
remoteObject.endpoint = "http://example.com/service";
remoteObject.test.addEventListener(ResultEvent.RESULT, _handleTestResult);
remoteObject.test.addEventListener(FaultEvent.FAULT, _handleTestFault);
var token = remoteObject.test(data);
}
If I'm reading your question correctly, you're trying to figure out how to access the actual data returned by the ResultEvent ?
If so, assuming you've made the call correctly and you've gotten data back in a format you're expecting:
private function _handleTestResult( event:ResultEvent ) : void
{
// you get the result from the result property on the event object
// edit: assuming the class Person exists with a property called name
// which has the value "John"
var person : Person = event.result as Person;
if (person.name == "John")
{
Alert.show("John: " + person.name);
}
else
{
Alert.show("Not John: " + person.name);
}
}
private function _handleTestFault( event:FaultEvent ) : void
{
// Maybe you know the type of the returned fault
var expectedFault : Object = event.fault as MyPredefinedType
if (expectedFault.myPredefinedTypesPredefinedMethod() == "BAR")
{
// something here
}
}
The ResultEvent has a property called result which will hold an instance of the object returned by the result (it might be the output of an XML file if using a web service, or a serialized object if using AMF, for example). This is what you want to access. Similarly, FaultEvent has a fault property that returns the fault information.
Edit: Changed code in _handleTestResult() in response to Gordon Potter's comment.