I'm trying to set up a class that has a dictionary with an int and my own object. On init I do not have anything to put in this dictionary.
class Menu
{
var emptyDic = Dictionary<String, String>()
var menuItem: Dictionary<Int,MenuItem>()
init()
{
//init object code
}
}
The emptyDic variable (which I got from StackOverflow) works fine, but I get an error consecutive declarations on a line must be separated by a ; if I mirror the same syntax for my menuItem dictionary. If I remove the () it complains that self.menuItem is not initialised.
I've not seen much written about dictionaries with other types other than strings. Is this a case for making it an optional or am I missing something more obvious?
Thanks
Andrew
You didn't copy the syntax. ':' and '=' are not equivalent. In this case, one specifies a type while the other specifies initialization.
Try:
var menuItem = Dictionary<Int,MenuItem>()
Related
In the code below, a reference type is being added. How can i do this value type?
imgList.Items.Add(imgList.Items[0]);
imgList.Items[imgIndex].Data = input; <== **This updates the 0th and the newly added element which is the issues**
Please advise
In order to avoid this issue, you need to clone imgList.Items[0] before adding it to imgList.Items. This basically involves creating a new object of the same type and populating it with data from the original.
The complexity of doing so depends on what the object is, but look at the answers to this question for some tips on cloning objects.
Edit: I forgot that .MemberwiseClone was protected.
You don't say in your code what the type of object is that you're adding to the list. If it's a class of yours, you can add a method to return a copy:
public MyType ShallowCopy()
{
return (MyType)this.MemberwiseClone();
}
and use
imgList.Items.Add(imgList.Items[0].ShallowCopy());
Or you can add a copy constructor:
public MyType(MyType original)
{
// Copy each of the properties from original
this.Data = original.Data;
}
and use
imgList.Items.Add(new MyType(imgList.Items[0]));
I'm trying to convert an image in my assets folder
"./assets/image1.png"
to type Object. It needs to be Object because that's what the function I'm using it in is expecting.
Any ideas what would be the simplest way to do this?
Do you mean something like :
[Embed(source="assets/logo.jpg")]
private var logo:Class;
private function init(e:Event):void
{
this.displayImage(logo as Object);
}
private function displayImage(img:Object):void
{
//Assuming you have an image control on stage with an instance
//name of "myImage"
myImage.source = img;
}
If the function you are passing the image to is expecting an Object object, you can in pass anything, it won't reject it. That doesn't mean the function will work correctly, though. Any value will be an Object (except for undefined, which will be accepted but coerced to null and maybe some other strange cases).
So, assuming you didn't write the function yourself, do you have any doc that describes what it expects? Or maybe you have the source code for it?. Otherwise, if the only thing you know about what this function expects is that the parameter must be of type Object... you're in trouble, I think.
Why don't you create a new Object containing the information about the image... including the path.
var obj:Object = new Object();
obj.path = "/assets/image.jpg";
obj.height = 32;
obj.width = 32;
trace(obj.path);
// or, if Flex
Alert.show(obj.path);
And then just pass the new Object into the function and access it like I have above.
I have a "format" method that works in a similar manner to the C# String.Format method, with the following signature:
In a class named StringTools:
/**
* Formats a string, based on C# String.Format method.
* #param raw A string with numbered tokens, such as "{0}, {1}"
* #param rest Values that replace the numbered tokens in raw.
*/
public static function format(raw:String, ...rest:*):String;
StringTools.format("{0}, {1}", "Hello", "World") returns the string "Hello, World" as expected. Now, I'm trying to get my logging class to use this method, but I'm having trouble passing the optional variables through. The signature of the method in the logging class is:
public static function infof(raw:String, ...rest:*):String;
If I pass "rest" directly into StringTools.format(raw, rest), it's passed in as an array, and not as a series of parameters, so if I call it liks this: infof("{0}, {1}", "Hello", "World"), I get the string "Hello,World, {1}", since it replaces the first token with the entire array of values.
I also tried constructing an arguments array, and calling the method like this:
var collectArgs:Array = [raw];
for (var i:Number = 0; i < rest.length; i++)
{
collectArgs.push(rest[i]);
}
var callFunction:Function = StringTools.format.call;
trace(callFunction.apply(null, collectArgs));
However, this traces "World,6". So, it looks like the parameters are shifted. So, I tried initializing collectArgs as [null, raw], and I get "Hello World,6. The number is {1}" again.
Am I doing something wrong? What is the correct way to pass optional parameters from one method that expects optional parameters to another method that expects optional parameters?
Thanks!
I think you are on the right lines using apply. This seems to do illustrate the behaviour you want:
static function f1(raw:String, ...rest:*):void
{
trace("f1: "+raw+" "+rest);
rest.unshift(raw);
f2.apply(null, rest);
}
static function f2(raw:String, ...rest:*):void
{
trace("f2: "+raw+" "+rest);
}
function passSomeArguments():void
{
f1("A",1,2,3);
}
EDIT: You need to pass 'null' as the 1st parameter to apply because the first parameter is what is considered to be 'this' when the function is called. Since the functions are static (and in any case have no dependency on 'this') you can pass null, but you must pass something.
You could also do something like this (of course this is not best implementation for the string formatting):
public static function format(raw:String, ...rest:*):String {
if (rest[0] is Array && rest.length == 1) {
rest = rest[0];
}
var r:RegExp = /(\{\d+\})/g;
var matches:Array = raw.match(r);
for (var i:Number = 0; i < rest.length; i++) {
raw = raw.replace(matches[i], rest[i]);
}
return raw;
}
Then your infof function would just look like this:
public static function infof(raw:String, ...rest:*):void {
var formatted = StringTools.format(raw, rest);
}
As mentioned in my comment, if you remove the call method from the end of you callFunction setter, then you do not need to supply null as the first argument. See http://livedocs.adobe.com/ to understand what the call method actually does, and what the first parameter is for.
As #stephen mentioned, it is a lot simpler to unshift your raw var onto the rest array, rather than building up a new one.
Actually, just found that it's my problem. It should work fine using the argument collection method described, as long as the first element in the arguments array is null. I'm not sure why null is necessary, but it works fine this way.
I am a Java programmer and need to work on a Flex/ActionScript project right now. I got an example of using ITreeDataDesriptor from Flex 3 Cookbook, but there is one line of actionscript code that's hard for me to understand. I appreciate if someone could explain this a little further.
public function getData(node:Object, model:Object=null):Object
{
if (node is Office) {
return {children:{label:node.name, label:node.address}};
}
}
The part that I didn't understand was "{children:{label:node.name, label:node.address}}". Office is simply a value object that contains two String properties: name and address.
The following return expression (modified from the question) ...
return {children:{label:node.name, body:node.address}}
... is functionally equivalent to this code ...
var obj:Object = new Object();
obj.children = new Object();
obj.children.label = node.name;
obj.children.body = node.address;
return obj;
The anonymous object returned in the question code complicates matters because it defines a property twice. In that case, the first declaration is used, and the subsequent one(s) are ignored. No compile-time or runtime error is thrown.
I think in Java you would call that a map or an associative array. In Javascript and Actionscript you can say this to create an object with certain properties:
var myobject = {
'prop1': 100,
'prop2': {
'a': 1
}
}
trace( myobject.prop1 ); // 100
trace( myobject.prop2.a ); // 1
In your example it's just returned as a nameless object.
return {children:{label:node.name, label:node.address}};
Means you are returning a new Object. The {} are the Object's constructor, and in this case its an Anonymous object.
Thank you both for the quick response. So if I understand your explanations correctly, the return statement is returning an anonymous object, and this object has only one property named "children", which is again an associative array - ok, here is the part I don't quite understand still, it seems that both properties in this array are named "label", is this allowed?
Data binding in ActionScript is really cool. But what if I want to refactor a big switch or if statement inside the curly braces into a function, for example:
{person.gender == 'male' ? 'Mr.' : 'Ms.'}
into:
{salutation(person)}
The compiler doesn't let me do that. I know about properties and I could write getters and setters on the person object. But since I am using inlined JSON objects now that's not convenient(I think). What are other good ways to refactor this code?
To answer Matt's comment. The data type of person is just plain Object. It was decoded from JSON format coming from a service call.
You'll need to make the Person class (assuming you have one) bindable in order for this to work.
However, since you are saying you're using JSON objects, I'm assuming you just have anonymous objects that were parsed from a JSON string. In that case, I'm pretty sure that won't work. You'll need to create a strongly typed object that has bindable properties.
Just FYI: to avoid having to write custom JSON parsers for every object you want to create, you can create strong typed objects from vanilla objects using a bytearray trick:
public static function toInstance( object:Object, clazz:Class ):* {
var bytes:ByteArray = new ByteArray();
bytes.objectEncoding = ObjectEncoding.AMF0;
// Find the objects and byetArray.writeObject them, adding in the
// class configuration variable name -- essentially, we're constructing
// and AMF packet here that contains the class information so that
// we can simplly byteArray.readObject the sucker for the translation
// Write out the bytes of the original object
var objBytes:ByteArray = new ByteArray();
objBytes.objectEncoding = ObjectEncoding.AMF0;
objBytes.writeObject( object );
// Register all of the classes so they can be decoded via AMF
var typeInfo:XML = describeType( clazz );
var fullyQualifiedName:String = typeInfo.#name.toString().replace( /::/, "." );
registerClassAlias( fullyQualifiedName, clazz );
// Write the new object information starting with the class information
var len:int = fullyQualifiedName.length;
bytes.writeByte( 0x10 ); // 0x10 is AMF0 for "typed object (class instance)"
bytes.writeUTF( fullyQualifiedName );
// After the class name is set up, write the rest of the object
bytes.writeBytes( objBytes, 1 );
// Read in the object with the class property added and return that
bytes.position = 0;
// This generates some ReferenceErrors of the object being passed in
// has properties that aren't in the class instance, and generates TypeErrors
// when property values cannot be converted to correct values (such as false
// being the value, when it needs to be a Date instead). However, these
// errors are not thrown at runtime (and only appear in trace ouput when
// debugging), so a try/catch block isn't necessary. I'm not sure if this
// classifies as a bug or not... but I wanted to explain why if you debug
// you might seem some TypeError or ReferenceError items appear.
var result:* = bytes.readObject();
return result;
}