Ruby-like Question: Make this function shorter (ActionScript 3) - actionscript-3

I just wrote this incredibly verbose code to turn numbers like 2 into 02. Can you make this function shorter, please (maintaning the functionality)?
public static function format(n:int, minimumLength:int):String {
var retVal:String = n.toString();
var stillNeed:int = minimumLength - retVal.length;
for (var i:int = 0; i < stillNeed; i++) {
retVal = "0" + retVal;
}
return retVal;
}
Please use types for variables. Extra points (good-vibe points, not SO points) if there's already a built-in function that I don't know about.
If anybody wants to post some extremely short equivalent in some other language, that would be fun too.

This wouldn't be the fastest implementation (it does some unnecessary copying and has a loop), but it is nice and readable:
public static function pad(num:int, minLength:uint):String {
var str:String = num.toString();
while (str.length < minLength) str = "0" + str;
return str;
}

I don't think there is a built-in way, but this might be cleaner (if not necessarily better performing):
//20 zeroes, could be more if needed
public static var Zeroes:String = "00000000000000000000"
public static function format(n:Number, minimumLength:int):String {
var retVal:String = (n.toFixed(0)); // cut off the decimals
var stillNeed:int = minimumLength - retVal.length;
retVal = Zeroes.substring(0, stillNeed) + retVal;
return retVal;
}
The "zeroes" var eliminates the need for looping, just prepend however many zeroes you need from a prebuilt string.

Christophe Herreman almost got it right, but his method adds more zeroes and not the differential amount. I fixed it a bit:
public static function format(n:int, minimumLength:int):String {
var v:String = n.toString();
var stillNeed:int = minimumLength - v.length;
return (stillNeed > 0) ? v : String(Math.pow(10, stillNeed) + v).substr(1);
}
My earlier try:
public static function format(n:int, minimumLength:int):String {
var stillNeed:int = minimumLength - n.toString().length;
return (n.split("").reverse().join("") as int) // 32 -> 23
*Math.pow(10, stillNeed > 0 ? stillNeed : 0).toString() // 23000
.split("").reverse().join(""); // 00032
}
public static function formatAny(n:Number, minimumLength:int):String {
return format((int)n) + n.toString().split('.')[ 1 ];
}
// use this if you want to handle -ve numbers as well
public static function formatAny(n:Number, minimumLength:int):String {
return (n < 0 ? '-' : '') + formatAny(n, minimumLength);
}

How about this:
public static function format(n:int, len:int):String {
var v:String = n.toString();
return (v.length >= len) ? v : String(Math.pow(10, len) + n).substr(1);
}
There is not built-in function to do this btw. If you need decent padding functions, take a look at the StringUtils in Apache Commons Lang.

Props to dirkgently and all others who have responded here, but apparently people are voting up without actually trying the code.
dirkgently's final function is mostly correct, but his '>' needs to be a '<'.
This function performs as desired (tested fairly thoroughly):
public static function format(n:int, minimumLength:int):String {
var v:String = n.toString();
var stillNeed:int = minimumLength - v.length;
return (stillNeed < 0) ? v : String(Math.pow(10, stillNeed) + v).substr(1);
}

"If anybody wants to post some
extremely short equivalent in some
other language, that would be fun too."
In javascript it is easy - paste this into your browser's address bar
javascript: function zit(n, w) {var z="000000000000000000"; return (z+n).substr(-w);} alert(zit(567, 9)); void(0);

I've always done this by taking a string that is the maximum padded width of zeros containing all zeros, then appeneding the string to padded to the end of the zeros string and then using substring to get the right hand Length digits.
Something like:
function pad(num:int, length:unit):String{
var big_padded:String "0000000000000000000000000000" + num.toString();
return big_padded.substring(big_padded.length - length);
}

The as3corelib package put out by adobe has a nice little NumberFormatter class that uses a series of STATIC classes. In this case you could use the addLeadingZero function.
//The following method is from the NumberFormatter class of the as3corelib package by Adobe.
public static function addLeadingZero(n:Number):String
{
var out:String = String(n);
if(n < 10 && n > -1)
{
out = "0" + out;
}
return out;
}
I included the function just to show it's simplicity, but I would use the package instead of yoinking the function because it provides many other useful features like StringUtils, encryption methods like MD5, blowfish, etc.
You can download the package here
For newer users you must provide a classpath to where this package lives. It is also smart to import the classes instead of using their fully qualified class names.

private function leadingZeros(value:int, numDigits:int):String
{
return String(new Array(numDigits + 1).join("0") + String(value)).substr(-numDigits, numDigits);
}

Related

Use my existing function to calculate new values without replacing existing ones

I've got this code that calculates tides. The function is huge and use the date and the port to calculate it.
There are 4 results :
haute1
haute2
basse1
basse2
Now, I'd like to calculate 4 new values (for date +1) without trigerring the all function with the new date, as it will change the value of haute1, haute2, basse1, basse2..
I only want to create new values "haute1_tomorrow" with the existing function.
Any idea if it's possible ?
What I was doing was calling the existing function like this :
maree("city, tomorrow_date);
with tomorrow_date as the date of tomorrow. But it would only trigger the function and set new values for haute1, haute2, basse1, basse2.
What I'd like is to keep these values, and create 4 new values haute1_tomorrow, haute2_tomorrow, basse1_tomorrow and basse2_tomorrow.
My function (I won't paste the entire function as it would be too long for you to read it) :
function get haute1():String { return $pmm; };
function get haute2():String { return $pms; }
function get basse1():String { return $bmm; };
function get basse2():String { return $bms; };
// convenient way to retrieve public data
function get results():Object
{
if (!$pmm) return {};
return {
haute1:$pmm,
haute2:$pms,
basse1:$bmm,
basse2:$bms,
};
}
function maree($inputport:String = "", $inputdate:String = "", onDataCompleteFunc:Function = null):void
{
if (onDataCompleteFunc != null) _onDataCompleteFunc = onDataCompleteFunc;
$port_maree = trim($inputport).toUpperCase();
/*if ($inputdate != '') $infodate = getdate(date2timestamp($inputdate + " 00:00:00","d/m/Y h:i:s"));
else $infodate = getdate(date2timestamp(date("dmY") + " 00:00:00","d/m/Y h:i:s"));
$jour_maree = $infodate["mday"];
$mois_maree = $infodate["mon"];
$annee_maree = $infodate["year"];*/
var $infodate:Array = $inputdate.split("/");
$jour_maree = Number($infodate[0]); // 25; // Day
$mois_maree = Number($infodate[1]); // 12; // Month
$annee_maree = Number($infodate[2]); // 2015; // Year
loadXmlData();
}
...
..
.
You need OOP: Object-Oriented Programming.
With no regard to your actual problem, the basic approach to problems like that is making a component/class that is self-sufficient to solve one instance of your problem. For example:
package
{
public class Trigonometry
{
public var degrees:Number;
public var radians:Number;
public var sine:Number;
public var cosine:Number;
public var tangent:Number;
public function Trigonometry(value:Number)
{
// Normalize degrees to -180..+180.
value %= 360;
if (value >= 180) value -= 360;
if (value < -180) value += 360;
degrees = value;
radians = value * Math.PI / 2;
sine = Math.sin(radians);
cosine = Math.cos(radians);
tangent = Math.tan(radians);
}
}
}
Then you work with instances which are independent and isolated and don't mess with each other's fields:
var A30:Trigonometry = new Trigonometry(30);
var A45:Trigonometry = new Trigonometry(45);
var A60:Trigonometry = new Trigonometry(60);
var A90:Trigonometry = new Trigonometry(90);
trace("Sine of 30 degrees is:", A30.sine);
trace("Cosine of 60 degrees is:", A60.cosine);
trace("Tangent of 45 degrees is:", A45.tangent);
UPD: Then with your task, you need something like that:
package
{
public class Mar
{
// Here you need to declare all the variables you are going to use.
// Like (I don't know what type you need):
public var $pmm:String;
public var $pms:String;
private var callback:Function;
// Then put all the methods you need:
public function maree($inputport:String = "", $inputdate:String = "", handler:Function = null):void
{
callback = handler;
// Your processing here.
}
}
}
Then you use it exactly the same:
var M1:Mar = new Mar;
var M2:Mar = new Mar;
M1.maree(/* first call arguments here */);
M2.maree(/* second call arguments here */);
So the instances will operate with no regard to each other existence, and you access to their respective fields like:
trace(M1.$pmm);
trace(M1.$pms);

How is this function return value being ignored in Actionscript?

I'm trying to port the Box2DFlashAS3 physics engine to another language (Xojo). I am not particularly fluent in Actionscript (but am more so than I am with C++ which Box2D was originally written in).
As I understand it, parameters passed to functions in Actionscript as done so by reference. Consider these two classes (greatly cut down for simplicity, it's the two GetInverse() functions I'm interested in):
public class b2Mat22 {
public function GetInverse(out:b2Mat22) : b2Mat22 {
var a:Number = col1.x;
var b:Number = col2.x;
var c:Number = col1.y;
var d:Number = col2.y;
var det:Number = a * d - b * c;
if (det != 0.0)
{
det = 1.0 / det;
}
out.col1.x = det * d; out.col2.x = -det * b;
out.col1.y = -det * c; out.col2.y = det * a;
return out;
}
public var col1:b2Vec2 = new b2Vec2();
public var col2:b2Vec2 = new b2Vec2();
}
and
public class b2Transform {
public function GetInverse(out:b2Transform = null) : b2Transform {
if (!out)
out = new b2Transform();
R.GetInverse(out.R);
out.position.SetV(b2Math.MulMV(out.R, position));
out.position.NegativeSelf();
return out;
}
public var position:b2Vec2 = new b2Vec2();
public var R:b2Mat22 = new b2Mat22();
}
I don't understand R.GetInverse(out.R); in the b2Transform class. Doesn't the GetInverse() function of the b2Mat22 class return a value? If so, why is it not being used?
That a function returns a value does not mean that this value needs to be used.
Here is an example:
Array - reverse()
Returns: Array — The new array.
Reverses the array in place.
var letters:Array = new Array("a", "b", "c");
trace(letters); // a,b,c
letters.reverse();
trace(letters); // c,b,a
You can see it modifies the array but it STILL returns the new array. This can be used for some techniques like method chaining:
myArray.reverse().concat(...) //you couldn't do this if it didn't return the new array, in that case you would have to do this:
// --->
myArray.reverse();
myArray.concat(...);
Also note that in AS (just like in JS, Java, etc.) the parameter is passed by reference if it is an object but the primitive values are not passed by reference.
Now to say the complete truth I have no idea what those methods from Box2D do, but I believe this might be the case.

Existing class to get property chain in ActionScript3?

Is there an existing class in Flash or Flex that gets the value from an object from it's property chain?
For example, something like this:
private function labelFunction(item:Object, column:GridColumn):String {
// where dataField equals "fields.status.name"
var value:String = PropertyChain.getValue(field, column.dataField);
return value;
}
~~~ Update ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I found this private method in the Binding class as well that could probably be used in a custom class:
/**
* #private
*/
private static function getFirstWord(destStr:String):String
{
// indexPeriod and indexBracket will be equal only if they
// both are -1.
var indexPeriod:int = destStr.indexOf(".");
var indexBracket:int = destStr.indexOf("[");
if (indexPeriod == indexBracket)
return destStr;
// Get the characters leading up to the first period or
// bracket.
var minIndex:int = Math.min(indexPeriod, indexBracket);
if (minIndex == -1)
minIndex = Math.max(indexPeriod, indexBracket);
return destStr.substr(0, minIndex);
}
I don't think there's an existing function. But it's very easy to build one, and it need not be restricted to generic Object sources, as any member of any object can be retrieved by name using square bracket notation. This simple version doesn't do any validation:
public static function getByName(root:*, member:String):* {
var memlist:Array = member.split('.');
var temp:* = root;
for(var i:uint = 0; i < memlist.length; i++)
temp = temp[memlist[i]];
return temp;
}
// And you can use this even on strongly-typed values, such as a MovieClip:
trace("stageWidth =", SomeUtil.getByName(mc, "stage.stageWidth"));

How can I check if an array contains another array?

At first sight it's very simple, but I'm having some problems to do this without using a lot of nested loops.
Example:
var father:Array = new Array(0,1,2,3,4,5);
var son:Array = new Array(3,4,5);
father.contains(son) // returns true or 4(the starting index if the contained array)
ActionScript 3 actually supports some slightly crazy stuff, due to the fact that, in the early days, Adobe/Macromedia were trying to make it compliant with Ecmascript.
So... you can do this:
var a1:Array = [1,2,3,4,5,6,7,8,9];
var a2:Array = [3,4,5];
// borrow String's indexOf function, and it magically works on Arrays
// but rename it because Array already has a different indexOf function
a1.indexOf2 = String.prototype.indexOf;
trace(a1.indexOf2(a2) > -1); // true
But you need to be a little bit careful because it will convert all the elements to Strings for the equality test. For primitives, it mostly won't matter but it will break badly with objects as they'll all be converted to "[object Object]" or to whatever their toString() returns.
Also, if you wanted to use the actual index for anything, rather than just checking it's not -1, you have to divide by two, as the number is double what you'd expect. I don't exactly know why this is :)
If you need something more general and reliable, you'd be better off writing a function to do an explicit search. This is a quick example, which I just wrote so could easily be bug-ridden:
public function find(haystack:Array, needle:Array):int
{
var index:int = -1;
while(index <= haystack.length - needle.length)
{
index++;
index = haystack.indexOf(needle[0], index);
for( var i:int = 1; i<needle.length; i++)
{
if(haystack[index+i] != needle[i])
{
continue;
}
}
if( i == needle.length)
{
return index;
}
}
return -1;
}
Try this for simplicity:
// Determines if an array contains en element (similar to the PHP function with the same name)
public function in_array(needle:*, haystack:Array):Boolean
{
for each (var element:* in haystack)
{
if (element == needle) {return true;}
}
return false;
}

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