Browse the notebook

Category: string manipulation

2D Array to String

function format2D(a:Array):String {
	var str:String="[";
	for (var i:int = 0; i<a.length; i++) {
		str+="["+a[i].toString()+"],";
	}
	str=str.substr(0,str.length-1);
	str+="]";
	return str;
}

// test it out
var array2D:Array = [[1,2,3], [4,5,6], [7,8,9]];

trace(array2D);
trace(format2D(array2D));

/* outputs:
1,2,3,4,5,6,7,8,9
[[1,2,3],[4,5,6],[7,8,9]]

*/

When you trace a 2D array to the output window the actual structure of the array isn’t clear. For some reason it just appears as a 1D array – this small function fixes that problem – it could also be used to save 2D arrays to text files etc…

Read snippet: 2D Array to String

Object Parsing (object from string #2)

var str:String = '{numA:100, numB:100, stringA:"hi there", object:{red:155, green:155, blue: 255}, array1D:[1, 2, 3, 4, 5, "six"], array2D:[[1,0,0], [0,1,0], [0,0,"z"]], color:0xFF0000}'

var obj:Object = toObject(str);//, true); // comment the "true" in for some debug info

// acess every value in the object for testing purposes
trace(obj.numA);
trace(obj.numB);
trace(obj.stringA);
trace(obj.object.red, obj.object.green, obj.object.blue);
trace(obj.array1D +" testing access: " + obj.array1D[3]);
trace(obj.array2D + " testing access: " + obj.array2D[1] + "  -  " + obj.array2D[2][2]);
trace(obj.color);

/*outputs:
100
100
hi there
155 155 255
1,2,3,4,5,six testing access: 4
1,0,0,0,1,0,0,0,z testing access: 0,1,0  -  z
16711680
*/
 
function toObject(str:String, debug:Boolean=false):Object{
	var cmds:Array = new Array();
	var stack:String = "";
	// skip first {
	str = str.replace(/^s+{|^{/, "");
	var obj:Object = new Object();
	for (var i:int = 0; i<str.length; i++){
		var char:String = str.charAt(i);
		if (char == ":"){
			cmds.push(["name:", stack])
			stack = "";
			char = "";
		}else
		if (char == "," || char == "}"){
			if (stack != ""){
			  cmds.push(["value:", stack]);
			}
			stack = "";
			char = "";
		}else
		if (char == '"'){
			stack = '"';
			for (j = i + 1; j <str.length; j++){
				char = str.charAt(j);
				stack+=char;
				i++;
				if (char == '"'){
					 break;
				}
			}
			cmds.push(["value:", stack]);
			stack = "";
			char = "";
		}else
		if (char == "{"){
			cmds.push(["object:", stack]);
			for (var j:int = i; j <str.length; j++){
				char = str.charAt(j);
				stack+=char;
				i++;
				if (char == "}"){
					 break;
				}
			}
			cmds.push(["value:", stack]);
			stack = "";
			char = "";
		}else
		if (char == "["){
			cmds.push(["array:", stack]);
			stack = "";
			char = "";
		}else if (char == "]"){
			if (stack != ""){
				cmds.push(["value:", stack]);
			}
			stack = "";
			char = "";
			i++;
		}
		stack += char;
	}
	// show some debug info
	if (debug){
		for (i = 0; i<cmds.length; i++) trace(cmds[i]);
	}
	// build the object
	var prop:String;
	var arrayMode:Boolean = false;
	var nest:int = 0;
	for (i = 0; i<cmds.length; i++){
		if (cmds[i][0] == "name:"){
			prop = cmds[i][1].replace(/s/g, "");
			arrayMode = false;
			nest = 0;
		}else if (cmds[i][0] == "value:"){
			if (arrayMode == false){
			  obj[prop] = valueType(cmds[i][1]);
			}else{
				// populate array
				if (nest == 1){
				  obj[prop].push(valueType(cmds[i][1]))
				}else{
				  obj[prop][nest - 2].push(valueType(cmds[i][1]))
				}
			}
		}else if (cmds[i][0] == "object:"){
			i++;
			obj[prop] = toObject(cmds[i][1], debug);
		}else if (cmds[i][0] == "array:"){
			arrayMode = true;
			nest++;
			if (nest == 1){
				// create array
				obj[prop] = new Array()
			}else{
				// nested array
				obj[prop].push(new Array());
			}
		}
	}
	return obj;
}
 
function valueType(val:*):* {
	if (isNaN(Number(val))) {
		   // remove leading and trailing white
		   // remove "" around strings
		   val = val.replace(/^s+|s+$/g,"").replace(/^"|"$/g,"");
		   //  see if it's a boolean
		   if (val == "true"){
			   val = true;
		   }else if (val == "false"){
			   val = false
		   }
	} else {
		val = Number(val);
	}
	return val;
}

This rather large code snippet parses ActionScript style Object syntax. While this is by no means a complete parser – it was easy to write and saved me a great deal of time on a project recently….

I specifically needed support for 1D and 2D arrays as Object properties. If that weren’t the case I would have used something simple like yesterdays method. The array support won’t work with anything higher than a 2D array (for now… may fix that soon). For fun I also added nested Object support – this doesn’t have any dimensional limitation so you can nest as many objects as you want. For instance.. this works just fine:

var gameUser:Object = toObject('{name:"Mr.S", alive:true, stats:{lives:100, points:1200, data:[100,12,40,12,90], plant:{branch:{leaves:["red, "green", "blue"]}}}');

trace(gameUser.stats.plant.branch.leaves[0]);
trace(gameUser.stats.lives);
// boolean support
trace("not true is false: ", !gameUser.alive);
/*outputs:
red
100
not true is false:  false
*/

You’ll also notice the added boolean support in the above example. Which was suggested by katopz in the comments of yesterdays post.

Definitely need to add a word of warning here… I wouldn’t be surprised if there were a bug or two lurking in this code. The Objects I’ve been parsing with it are all quite similar… so use at your own risk.

UPDATEs – Known Issues:
1) fixed a bug where if a String property contains a ,{}[] or : character the parser failed.

Read snippet: Object Parsing (object from string #2)

Object from String

var str:String='{x: 10, y:10, width:100, heigth:100, name:"myClip", type:"clip"}';

var obj:Object=toObject(str);

trace(obj.x + obj.width, obj.name, obj.type);

function toObject(str:String):Object {
	str=str.replace(/{|}/g,"");
	// to an array of name value pairs [0] = name, [1] = value etc...
	var pairs:Array=str.split(/:|,s+/);
	var obj:Object = new Object();
	for (var i:int = 0; i<pairs.length; i+=2) {
		obj[pairs[i]]=numOrString(pairs[i+1]);
	}
	return obj;
}
function numOrString(val:*):* {
	if (isNaN(Number(val))) {
		if (val.charAt(0) == '"' && val.charAt(val.length - 1) == '"'){
			val = val.substr(1, val.length - 2);
		}
	} else {
		val=Number(val);
	}
	return val;
}

A very limited way to parse an object from a string… Tomorrow I’ll post a better version of this that’s a good deal more complex and doesn’t really use regular expressions… This works nicely if you don’t have arrays as Object properties…

Read snippet: Object from String

Formating Numbers with Commas

trace(formatNum(1000));
trace(formatNum(10000000));
trace(formatNum(1000000.39485));

function formatNum(num:Number):String {
	var newStr:String = "";
	var str:String = num.toString();
	
	var parts:Array = str.split(".");
	str = parts[0];
	var end:String = (parts[1]) ? "." + parts[1] : "";
	
	var i:int = str.length;
	while(i-- > 0){
		 var char:String = str.charAt(i);
		 if ((str.length - i) % 3 == 0){
			newStr = "," + char +newStr;
		 }else{
			newStr = char +  newStr; 
		 }
	}
	return newStr + end;
}
/*
outputs:
1,000
10,000,000
1,000,000.39485
*/

The function formatNum() returns a string given a number argument … it adds commas where appropriate and takes into account decimal values. This is useful anywhere you need to display numbers over 1,000, in a game, in a calculator app etc…

Read snippet: Formating Numbers with Commas

String.indexOf()

var words:String = "one two three four five";

trace("two: ", words.indexOf("two"));

var letters:String = "abcd";

trace("d: ",letters.indexOf("d"));

trace("z: ",letters.indexOf("z"));

/*
outputs:
two:  4
d:  3
z:  -1
*/

indexOf() searches a string for another string and returns an index… in line 3 above, I search the words string for the smaller string “two” and indexOf() gives me the index of the letter “t”. If indexOf() doesn’t find anything it will return -1 (as in the case of line 9).

I seem to recall using this in some unexpected places. I’ll see if I can dig up an example over the next few days.

Read snippet: String.indexOf()

Zig Zag Binary ix2 & i/2

var txt:TextField = TextField(addChild(new TextField()));
txt.text = "";
txt.width = 190;
txt.height = 400;
txt.multiline = true;

var count:int = 1;
function render():void{
	var line = int(count).toString(2);
	while(line.length < 31){
		line = "0" + line;
	}
    txt.appendText(line + "n");
	txt.scrollV= txt.maxScrollV;
}

addEventListener(Event.ENTER_FRAME, onCountUp);
function onCountUp(evt:Event):void {
	count *= 2;
	render();
	if (count ==0x40000000){
		removeEventListener(Event.ENTER_FRAME, onCountUp);
		addEventListener(Event.ENTER_FRAME, onCountDown);
	}
}
function onCountDown(evt:Event):void {
	count /= 2;
	render();
	if (count ==1){
		addEventListener(Event.ENTER_FRAME, onCountUp);
		removeEventListener(Event.ENTER_FRAME, onCountDown);
	}
}

The above animates a zig zag pattern in a TextField.

Read snippet: Zig Zag Binary ix2 & i/2

N to the power of N (with strings)

var input:String =  "a, b, c";

var words:Array = input.split(", ");
var max:String = "";
var maxD:String =  (words.length - 1).toString();
for (var i:int = 0; i<words.length; i++){
	max += maxD;
}
var maxInt:int = parseInt(max,words.length);

for(i = 0; i<=maxInt; i++){
	 var indices:String = i.toString(words.length);
	 var r:String = "";
	 var k:int=0;
	 for (var j:int = 0; j<indices.length; j++){
		 r += words[parseInt(indices.charAt(j))] +" ";
		 k++;
	 }
	 while(k < words.length) {
		r = words[0] +" "+ r;
		k++;
	 }
	trace(r);
}
trace(i, " variations");

Like many things on this site, I coded this rather quickly and it can probably be cleaned up…

Setting the input variable of the above snippet to “a, b” will output:

a a
a b
b a
b b
4 variations

Setting the input to “a, b, c” will output:

a a a
a a b
a a c
a b a
a b b
a b c
a c a
a c b
a c c
b a a
b a b
b a c
b b a
b b b
b b c
b c a
b c b
b c c
c a a
c a b
c a c
c b a
c b b
c b c
c c a
c c b
c c c
27 variations

I created this to work with words… inputs like “bread, breath, blobs, backwards”. Be careful because you can quickly get millions of outputs:

1 to the power of 1 is 1
2 to the power of 2 is 4
3 to the power of 3 is 27
4 to the power of 4 is 256
5 to the power of 5 is 3125
6 to the power of 6 is 46,656
7 to the power of 7 is 823,543
8 to the power of 8 is 16,777,216
etc…

Read snippet: N to the power of N (with strings)

MYSQL SOUNDEX

SELECT * FROM _users WHERE SOUNDEX(name) LIKE SOUNDEX('jon');

Not ActionScript, but the coolest thing I’ve seen in Mysql in awhile, the soundex function will convert a string into a soundex index… read more about it on wikipedia.

I did some tests and it matches things like:

what’s your name? & whats yer name

very cool… since this isn’t ActionScript, I will post another snippet right after this.

UPDATE: Actually started reading more about this stuff and dug up some algorithms… metaphone etc… fun stuff, maybe I’ll port some of it to actionscript…

Read snippet: MYSQL SOUNDEX

split() hacks & RegExp

var path:String = "section/home.swf";

var deepLinkValue:String = path.split("/")[1].split(".swf")[0];

trace(deepLinkValue);
/*outputs:
home
*/

As a primarily self taught programmer there was a time when I didn’t know what a regular expression was. There was also a time when ActionScript didn’t have them (you had to use a library). Sometimes when I’m in a rush I still do weird things like the above instead of regular expressions… pretty nasty I know.

Here are two different regular expressions that do the same thing:

var path = "section/home.swf"

trace(path.match(/w+(?=.)/))
/*outputs:
home
*/
 
trace(path.replace(/.+/|..+/g, ""))
/* also outputs:
home
*/

Actually, I’d be curious to see other regular expressions that only return one value (not an array) that achieve this same thing… feel free to post of a comment if you can think of one…

Read snippet: split() hacks & RegExp