How to find an ArrayCollection item with a specific property value?

35,636

Solution 1

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.

Solution 2

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;

Solution 3

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

Solution 4

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

Solution 5

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.

Share:
35,636
Giorgio Gelardi
Author by

Giorgio Gelardi

Updated on July 05, 2022

Comments

  • Giorgio Gelardi
    Giorgio Gelardi about 2 years

    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?

  • Matt Dillard
    Matt Dillard over 14 years
    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.
  • Eric Belair
    Eric Belair over 14 years
    Why not be helpful, and actually show the user the right way to do it, rather than simply telling them "No, you can't"?
  • Amarghosh
    Amarghosh over 14 years
    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.
  • Pimenta
    Pimenta about 12 years
    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.
  • tigeryan
    tigeryan over 11 years
    Very useful and very quick on large collections! Thanks much!
  • adarshk
    adarshk over 10 years
    very optimized way for large Array Collections.