5

I have some XML structures like this:

var struct:XML = <mh>
  <mi id="1" stuff="whatever"/>
  <mi id="2" stuff="whatever"/>
  <mi id="3" stuff="whatever"/>
</mh>;

I know I can access a subnode by "id", in this way:

var stuff:Object = struct.(hasOwnProperty('@id') && @id == '2').@stuff;

Now I have some similar ArrayCollection structure:

private var cmenu:ArrayCollection = new ArrayCollection([
    {id:"1", stuff:"whatever"},
    {id:"2", stuff:"whatever"},
    {id:"3", stuff:"whatever"}
]);

I wonder if items can be accessed in a similar way, like this:

var stuff:Object = cmenu['id == 2'].stuff;

Is it possible?

3
  • All the answers below are right. There's no magic bullet hash syntax you can use to pull a specific object by property, so you just have to do a series of loops to find your properties. Commented Oct 14, 2009 at 15:16
  • With a couple helper functions you can get pretty close though. Commented Oct 14, 2009 at 16:03
  • I want to thank you for all the answers, I already know about the helper functions but, after all, I don't have to use Object/Array/ArrayCollection at any cost, so I managed to convert all that stuff in xml. Commented Oct 15, 2009 at 14:28

7 Answers 7

18

You can generalize Matt's answer a bit so that you can pass in the ID value you want instead of hard-coding it, and only need a single line to get your match (I assume you may want to do this in multiple places).

First you'd write a function to generate your find function:

function findId(id:int):Function {
  return function( element : *, index : int, array : Array ) : Boolean
  {
    return element.id == id;
  }
}

Then I'd write a function to return the first match so you don't have to duplicate the two lines:

function findInCollection(c:ArrayCollection, find:Function):Object {
  var matches : Array = c.source.filter( find );
  return ( matches.length > 0 ? matches[0] : null );
}

Then you'd just do this:

var stuff:String = findInCollection(cmenu, findId(2)) as String;
Sign up to request clarification or add additional context in comments.

4 Comments

Wow, neat use of that function factory. I've always felt there must be a way to customize the callback routine; this is really elegant.
This has been very useful for me since Array Collections are not as straight forward in Flex/AS3 as in other languages. Thanks @Herms for your simple solution on this. I'm using it for an XML service with local file.
Very useful and very quick on large collections! Thanks much!
very optimized way for large Array Collections.
4

I've always used filterFunctions for ArrayCollections:

private var cmenu:ArrayCollection = new ArrayCollection([
    {id:"1", stuff:"whatever"},
    {id:"2", stuff:"whatever"},
    {id:"3", stuff:"whatever"}
]);

function getItemFromCollection(id:String):Object {
    var cmenuFiltered:ArrayCollection = new ArrayCollection(cmenu.toArray());

    cmenuFiltered.filterFunction =
        function(item:Object):Boolean {
            return item.id == id;
        }

    cmenuFiltered.refresh();

    return cmenuFiltered.getItemAt(0);
}

Comments

3

No, you can't. struct.mi.(@id == "2").@stuff is E4X which is short for ECMA Script for XML. You can't use e4x on other AS objects.

2 Comments

Why not be helpful, and actually show the user the right way to do it, rather than simply telling them "No, you can't"?
When I saw this thread, there were answers explaining how to do it - why would I do that again? But I thought it would be good to let OP know why the "@syntax" was not possible on array collections and other AS objects in general. And apparently OP was "wondering if items can be accessed in a similar way" - so a "no" along with "why not" seemed a good answer.
3

If you look at the docs for the Array class, you'll find several routines that aid in this, but none as concise as the e4x syntax used by the built-in XML data type. The filter() method in particular sounds like it might be the best fit for your example.

Here's a sample for how you might do this, given your setup.

var matches : Array = cmenu.source.filter( findId2 );
var stuff : Object = ( matches.length > 0 ? matches[0] : null );

...and the callback function findId2:

function findId2( element : *, index : int, array : Array ) : Boolean
{
    return element.id == 2;
}

Comments

2

You can actually go a little bit further and roll the filter function up into the findInCollection function. This means you can just pass the ArrayCollection, property name, and value in the same call:

public function findInCollection(c:ArrayCollection, 
                       propertyName:String, propertyValue:*):Array {

    var matches : Array = c.source.filter( 
               function( element : *, index : int, array : Array ) : Boolean {
                 return element[propertyName] == propertyValue;
               } 
            );

    return matches; 

}

var ac:ArrayCollection=new ArrayCollection(
                [{name:'Ben', id:1, age:12},
                    {name:'Jack', id:2, age:22},
                    {name:'Jill', id:4, age:22},
                    {name:'Joe', id:3, age:17}
                ]
            );
var searchedElements:Array=findInCollection(ac,'age',22);

Returns an array containing the 'Jack' and 'Jill' objects.

Obviously this only works if you are looking for a single property value, but if you wanted to search on multiple properties it would be possible to pass through an object containing the properties and values to search for.

Comments

0

You can even more generalize Herms answer by adding the property name in the function like this and thus getting a rather generic way of searching by property in an ArrayCollection

private function findInCollection(c:ArrayCollection, findFunction:Function):Array
{
 var matches : Array = c.source.filter( findFunction );
 return matches;
}

private function findFunction(propertyName:String,propertyValue:*):Function
{
   return function( element : *, index : int, array : Array ) : Boolean
   {
    return element[propertyName] == propertyValue;
   }
}

with the following usage

var ac:ArrayCollection=new ArrayCollection(
    [{name:'Ben', id:1, age:12},
     {name:'Jack', id:2, age:22},
     {name:'Joe', id:3, age:17}
    ]
);
var searchedElements:Array=findInCollection(ac,findFunction('id',2));

it will return an Array with the following object

{name:'Jack', id:2, age:22}

The drawback of this method is that we hard-code the property name with a String. This may be harmful for code maintenance.

Comments

0
'private var cmenu:ArrayCollection = new ArrayCollection([
    {id:"1", stuff:"whatever"},
    {id:"2", stuff:"whatever"},
    {id:"3", stuff:"whatever"}
]);

for(var i:int=0;i<cmenu.length;i++){
var num:Number=cmenu.getItemAt(i).id;
if(num==2){
var stuf:String=cmenu.getItemAt(i).stuff;
}
}'

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.