Flex How To Call A Function With A Variable Number Of Parameters? - apache-flex

Let's Say I Have This Class:
package{
import flash.display.Sprite;
public class Main extends Sprite{
public function Main(){
trace(getAverage(1,2,3));
trace(getAverage(1,2,3,4));
trace(getAverage(1,2,3,4,5));
}
public function getAverage (...numbers) {
var total = 0;
for (var i = 0; i < numbers.length; i++) {
total += numbers [i];
}
return total / numbers.length;
}
}
}
How do I accomplish the "opposite" of this? Namely, how could I now CALL 'getAverage' with a dynamic number of paraemters?
For instance, if I wanted to do something LIKE:
var r:int=Math.random()*6;
var a:Array=new Array();
for (i:int=0;i<r;i++) {
a[i]=Math.random()*22;
}
// Now I have 'r' Number Of Parameters Stored In 'a'
// How Do I Call getAverage, with all the values in 'a'??
// getAverage(a) isn't right, is it?
// I'm looking for something similar to getAverage(a[0],a[1],a[...]);
var av:Number=getAverage(???);
What I want to know, is if I have a function that takes a variable number of arguments, that's great, but how can I CALL IT with a variable number of arguments, when that number isn't known at runtime? Possibly it's impossible... I'm just not sure, since 'callLater' seems to be able to take an array and generate a dynamic number of parameters from it somehow...
NOTE: Answers consisting solely of "Why Do You Want To Do This?", will be downvoted.
P.S. This IS NOT about calculating Averages! I REALIZE There Are Way Simpler Ways Of Doing All Of This! (I could just write getAverage to accept a single array as its only parameter) The Above is just an EXAMPLE to Illustrate my Question. HOW TO PASS A DYNAMIC NUMBER OF PARAMETERS TO A FUNCTION?

Is this what you're looking for?
var av:Number = getAverage.apply(null, a);

Dave is correct. You can use the apply method of a function to pass in an Array of arguments.
Here is a better explanation of how it works and what the arguments of apply are:
http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/Function.html
Also note that you can use the call method to do the same thing but using ...args (comma-delimited list) instead, but apply would be more suitable to your situation.

The problem with your question is that the arguments object is already an Array and using (...args) already provides you with a dynamic way to pass any number of arguments you require. Sorry about the previous answer, wasn't thinking straight...

You can create an array or an object with those parameters and pass that object to that function. That's just normal.

Flash has a rather strong introspection capabilities. So, instead of passing a number of objects, you just pass a single dynamic object with any number of attributes you need:
var ob:Object={arg1:"value1", arg2:8};
var arg:String="arg4";
ob["arg3"]=8;
ob[arg]=18;
trace (ob.hasOwnProperty("arg1"));
trace (ob.arg3);
trace (ob.arg4);
That should cover just about any use case you might need. The downside is that this allows for some rather clever and hard to trace bugs. :-)

Related

Java: Using .stream to transform ArrayList<MyObj> to TreeMap<String, ArrayList<MyObj>>

This is a Java 8 lower-intermediate question:
I have the following code in Java 6:
List <ViewWrapperContentElementTypeProperty> vwPropertyList = getFromDao();
TreeMap <Long, ArrayList<ViewWrapperContentElementTypeProperty>> mappedProperties = new TreeMap<Long, ArrayList<ViewWrapperContentElementTypeProperty>> ();
for (ViewWrapperContentElementTypeProperty vwCetP:vwPropertyList)
{
if(null==mappedProperties.get(vwCetP.getContentElementTypeId()))
{
ArrayList<ViewWrapperContentElementTypeProperty> list = new ArrayList<ViewWrapperContentElementTypeProperty>());
list.add(vwCetP);
mappedProperties.put(vwCetP.getContentElementTypeId(), list);
}
else
{
mappedProperties.get(vwCetP.getContentElementTypeId()).add(vwCetP);
}
}
Can I use vwPropertyList.stream().map() to implement this more efficiently?
It seems like you are looking for a grouping by operation. Fortunately, the Collectors class provide a way to do this:
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toCollection;
...
TreeMap<Long, ArrayList<ViewWrapperContentElementTypeProperty>> mappedProperties =
vwPropertyList.stream()
.collect(groupingBy(ViewWrapperContentElementTypeProperty::getContentElementTypeId,
TreeMap::new,
toCollection(ArrayList::new)));
I used the overloaded version of groupingBy where you can provide a specific map implementation (if you really need a TreeMap).
Also the toList() collector returns a List (which is an ArrayList but it's an implementation details). Since you apparently need to specify a concrete implementation as you want ArrayLists as values, you can do it with toCollection(ArrayList::new).
With regard to using Streams and lambda expressions, of course... This should look like the following:
Map<Long, List<ViewWrapperContentElementTypeProperty>> mappedProperties =
vwPropertyList.stream()
.collect(Collectors.groupingBy(ViewWrapperContentElementTypeProperty::getContentElementTypeId));
Please note that using Stream API methods like above forces using interfaces (Map, List), which is a good practice anyway.
When it comes to performance, it should be roughly the same as using a traditional loop.

Create and define Vector

I'm looking for method to create Vector and push some values without defining variable Vector. For example:
I have function:
public function bla(data:Vector.<Object>):void { ... }
this function expects Vector as parameter. I can pass parameters this way
var newVector:Vector.<Object> = new Vector.<Object>();
newVector.push("bla1");
newVector.push("bla2");
bla(newVector);
Can I do it in one line in Flex? I'm looking for something like:
bla(new Vector.<Object>().push("bla1").push("bla2"));
I've also tried this:
bla(function():Vector.<Object> { var result:Vector.<Object> = new Vector.<Object>(2, true); result.push("bla1"); result.push("bla2"); return result; });
But it complains:
1067: Implicit coercion of a value of type Function to an unrelated type __AS3__.vec:Vector.<Object>...
Thanks
You can't chain Vector.push() calls as they return uint's -- the new vector length.
The coercion problem, on the other hand, happens because you are passing a function to the bla function, which expects a Vector.<Object>.
You could fix that easily:
bla((function():Vector.<Object> {
var result:Vector.<Object> = new Vector.<Object>(2, true);
result.push("bla1");
result.push("bla2");
return result; })()
);
However, there's already a top level function in AS3 that helps you creating vectors.
The Vector() function expects either an Array or a Vector and returns a Vector. So, for example, you could use:
bla(Vector.<Object>(['bla1', 'bla2']));
Visit the AS3 Reference for more info.
EDIT: I forgot to mention that the fix on the function approach was simply adding a () to it, meaning we actually called the anonymous function and passed it's return to the bla function.
Just want to mention push takes multiple arguments, and each one is pushed onto the stack sequentially:
function getVector():Vector.<String>
{
var newVector:Vector.<String> = new Vector.<String>();
newVector.push("blah1","blah2","blah3","blah4");
return newVector;
}
I'm not really sure why you'd need to do it one line. You could always write a wrapper class if you happen to do this often. The wrapper class could have a push method that returns a reference to the original object so you can use the first method you wanted.
You could also write a helper function which created a new vector and added the elements to the vector and then returned the vector.
Is there a particular need for wanting this on one line?
You are not able to do this:
bla(new Vector.<Object>().push("bla1").push("bla2"));
because the "push" method returns the length of the Vector. So what this means is that you are trying to push the String "bla2" onto the int 1. This won't work!
And your next example is passing a function to the bla method, not calling that function and passing the returned Vector.
Also you are saying the Vector type is "Object" but you are passing in Strings. You should do this:
Vector.<String>
You could do something like this:
function getVector():Vector.<String>
{
var newVector:Vector.<String> = new Vector.<String>();
newVector.push("bla1");
newVector.push("bla2");
return newVector;
}
bla( getVector() );

How can I determine a function's argument count at runtime in Flex 3?

I want to pass an optional data parameter to some callbacks, but only to callbacks that support a single parameter; right now, I have a moderately-sized code base of callbacks that cannot accept a parameter at all. How can I check what parameters a Function object supports?
Function is an Object.
Every function has a read-only property named length that stores the number of parameters defined for the function.
Use it.
If your function is declared in a class use the function describeType it will return an XML you can parse and look at your function name with his arguments
The arguments array is an array of all the parameters passed into a function. Maybe that is what you are looking for?
function traceArgArray(x:int):void
{
for (var i:uint = 0; i < arguments.length; i++)
{
trace(arguments[i]);
}
}
Example taken from livedocs.adobe.com

How do I find the length of an associative array in ActionScript 3.0?

Is there a simple way to retrieve the length of an associative array (implemented as an Object) in ActionScript 3.0?
I understand that there are two primary ways of creating associative arrays in AS3:
Use a Dictionary object; especially handy when the key does not need to be a string
Use an Object, and simply create properties for each desired element. The property name is the key, and the value is, well, the value.
My application uses approach #2 (using the Object class to represent associative arrays).
I am hoping there is something more native than my for loop, which manually counts up all the elements.
You have to count them in a for loop as you do. Of course, you could make a class and stick the for loop in that class.
For some great implmentations of Collections in AS3, check these guys.
Edit 2013 Not surprisingly, links do break after time. Try this new one: http://www.grindheadgames.com/get-the-length-of-an-object.
Doing a few tests on this has actually surprised me. Here's normal use of an Array:
var things:Array = [];
things.push("hi!");
trace(things.length);
// traces 1
trace(things);
// traces hi!
Here's if we set a value to a string:
var things:Array = [];
things["thing"] = "hi!";
trace(things.length);
// traces 0
trace(things);
// traces an empty string
trace(things["thing"]);
// traces hi!
Basically if you add things using strings you're setting properties rather than actually adding to the array. Makes me wonder why Array is dynamic in this way.
So... yeah count the items with a for ... in loop!
I think you're stuck with counting them "manually".
An option would be to wrap the whole thing in a class and keep a separate variable that you update as you add/remove.
var count:int;
var key:String;
for (key in myObject)
{
count++;
}
trace ("myObject has this many keys in it: " + count);
or, alternatively, the for-each syntax (I haven't tested to see which is faster)
for each (var o:* in myObject)
{
count++;
}

what is the flex (ActionScript3) syntax for a function valued function's type?

What is the syntax to declare a type for my compare-function generator in code like the following?
var colName:String = ""; // actually assigned in a loop
gc.sortCompareFunction = function() : ??WHAT_GOES_HERE??
{
var tmp:String = colName;
return function(a:Object,b:Object):int { return compareGeneral(a,b,tmp); };
}();
Isn't "Function" a data type?
In order to understand what the data type is, we must know what the intended outcome of the return is. I need to see the code block for compareGeneral, and I still don't believe this will help. You have two returns withing the same function "gc.sortCompareFunction", I believe this is incorrect as return gets a value and then acts as a break command meaning the rest of the anything withing the same function block is ignored. The problem is that I don't know which return is the intended return, and I don't know that flash knows either. You can use * as a data type, but this should only really be used in specific situations. In this situation I believe you need only the one return value that merely returns whatever the value of compareGeneral.
Now if this is a compareGenerator it really should either return a Boolean TRUE or FALSE, or a int 0 or 1, lets use the former. Also I believe we can use one less function. Since I have not seen all of your code and I am not exactly sure what your trying to accomplish, the following is hypothetical.
function compareGeneral(a:object,b:object):Boolean
{
//Check some property associated to each object for likeness.
if(a.someAssignedPropery == b.someAssignedPropery)
{
return true;
}
return false;
}
var objA:Object = new Object();
objA.someAssignedProperty = "AS3";
objB.someAssignedProperty = "AS3";
compareGeneral(objA,objB);
In this case compareGeneral(objA,objB); returns true, though we haven't done anything useful with it yet. Here is a way you may use it. Remember that it either returns a value of true or false so we can treat it like a variable.
if(compareGeneral(objA,objB)) //same as if(compareGeneral(objA,objB)) == true)
{
trace("You have found a match!");
//Here you can call some other function or set a variable or whatever you require functionality wise based on a match being found.
}
else
{
trace("No match could be found!");
}
I hope that this is able to help you understand data types and return values. I do not know what you were doing with tmp, but generally functions that return a value deal with that one thing and only that thing, so it is best that the compare function compare one thing against the other and that be the extent of the call. Whatever functionality you require with tmp can go inside its own function or method, and be called when needed.

Resources