views:

6673

answers:

6

How can I convert the following XMLList to an Array of Strings without using a loop?

<labels>
    <label>All</label>
    <label>your</label>
    <label>base</label>
    <label>are</label>
    <label>belong</label>
    <label>to</label>
    <label>us.</label>
</labels>

I want this result:

["All","your","base","are","belong","to","us."]

Right now, I am doing the following:

var labelsArray:Array /* of String */ = [];

for each (var labelText:String in labels.label)
{
    labelsArray.push(labelText);
}

I was wondering if there is a simpler way to do this in ActionScript 3.0

+1  A: 

for loops are extremely fast in AS. Why'd you need that? But you could give this a try:

private function toArray():void {
    var xml:XML = <labels>
               <label>all</label>
               <label>your</label>
              </labels>;

    var array:* = xml.label.text().toXMLString().split("\n") ;
    trace(array as Array);
}
dirkgently
Don't "need" it, per se. I was just wondering if there was a more elegant/simpler way to do it - i.e. a one line command.
Eric Belair
Maybe you could now give this function a try :)
dirkgently
Splitting on a new line? Looks like a bad idea to me. What if there's a newline in the label?
Wouter Coekaerts
Not the best, I admit. It's a hack. as I've said, something can be done :)
dirkgently
'array' does not need to be typed as any (*) since String.split() returns an Array.
Richard Szalay
This implementation has less visible code, but more work since a string is created (needlessly) by toXMLString() and then split.
Richard Szalay
@Richard: The * is used since a very similar technique can be used to convert it to a XMLList.
dirkgently
True, but this code does not use an XMLList, resulting in two type coercions when none were necessary.
Richard Szalay
Personally I recommend a plain `XMLList` over an `XML` object, which would eliminate most of the need to convert the labels in an array of labels.
dlamblin
@dlamblin: I would use them too -- but they cause heartburn at times. So beware.
dirkgently
+1  A: 

Your current implementation is more than sufficient. The only optimisation you could make (though I wouldn't bother unless you are using Vector.<>) is to pass in the initial capacity into the Array constructor:

var xmlLabels : XMLList = labels.label;
var labelsArray:Array /* of String */ = new Array(xmlLabels.length);

var index : int = 0;

for each (var labelText:String in xmlLabels)
{
    labelsArray[index++] = labelText;
}
Richard Szalay
A: 

i feel like there's a one-liner for this out there somewhere... oh well.

question: why does .length fail here? (always 0)

 public static function xmlListToArray($x:XMLList):Array {
  var t:int = $x.length;
  var a:Array=new Array(t), i:int;
  for (i = 0; i < t; ++i) a[i] = $x[i];
  return a;
 }
one giant media
for XML length is a method, not a property. it should be var t:int = $x.length(). Not sure, but I think you get 0 because it is trying to find a length node, can't so returns nothing, which when cast with int becomes 0.
invertedSpear
+2  A: 

This one works pretty well:

public static function xmlListToArray($x:XMLList):Array {     
    var a:Array=[], i:String;
    for (i in $x) a[i] = $x[i];
    return a;
}
one giant media
A: 

Despite the earnest uses of for loops and logically working on the XML object as given, this is a job for XMLList.
It would best look something like this:

var xml:XML = 
<labels>
    <label>All</label>
    <label>your</label>
    <label>base</label>
    <label>are</label>
    <label>belong</label>
    <label>to</label>
    <label>us.</label>
</labels>
;
var list:XMLList = xml.label;
var labels:XMLList = list.text(); //Optional
trace(list[0]);
trace(list[3]);
trace(list[6]);

This would output:

All
are
us.

I've confirmed this in flash myself. Personally it makes sense to me to use the optional line, and reference labels[0] etc. but that's not needed here.

I know you're asking for an array of strings as the output, but basically I'm asking you why you can't just use the array accessors of an XMLList object.

Here's a fine walk-through on that: Senocular on E4X.

dlamblin
I'll have to try this one out...
Eric Belair
@Eric Belair if you tried it as it was you probably found issues with `x` being defined in most `movieClips` already and `XMLLists` not type coercing to `Arrays` as I expected. I have fixed this, and continue to insist that the `XMLList` way is the better way over `for` loops and string `splits`.
dlamblin
A: 

This works good but uses some odd syntax of the XMLList. The last statement can be placed on one line if desired.

    var labels:XML = <labels>
                    <label>All</label>
                    <label>your</label>
                    <label>base</label>
                    <label>are</label>
                    <label>belong</label>
                    <label>to</label>
                    <label>us.</label>
                </labels>;

var labelsArray:Array /* of String */ = [];

labels.label.
(
              labelsArray.push(toString())
);  

The toString() call can be replaced with an attribute() call to pull out attributes.

Densefog