The original Flex’s ListCollectionView is a brilliant tool that brings together flex data binding magic and some functional programming abilities. ListCollectionView instances can filter data, sort data and can be joined into chains.
So, it seems quite natural to have “map” – the most powerfull tool of traditional “sort/grep/map” triade – in databinding toolbox. Unfortunatelly, the original ListCollectionView has only “sort” and “filter” features. Here is an approach to fill this gap:
The ListCollectionMap class is a variation of ListCollectionView, and can be used in data binding chains mentioned earlier.
It doesn’t know how to filter and sort things, instead it maps data using the mapping function passed in constructor.
Here is the example:
public var dataIntegers:ArrayCollection
= new ArrayCollection([1, 2, 3]);
public var mapIntegers:ListCollectionMap
= new ListCollectionMap(incrementMapping, dataIntegers);
public function incrementMapping(i:int):int
{
return i + 1
}
The mapIntegers collection initially contains [2, 3, 4] and remaps its elements once the source elements are changed. E.g. dataIntegers.addItem(5) will cause the update resulting in adding ’6′ to mapIntegers.
Lets now look at the more interesting example, one-to-many mapping:
public var dataObjects:ArrayCollection
= new ArrayCollection([{a: 1, b: 2}, {a: 3, b: 4}, {a: 5, b: 6}]);
public var mapObjects:ListCollectionMap
= new ListCollectionMap(split, dataObjects);
public function split(o:Object):Object
{
return ListCollectionMapResult.fromArray([{c: o.a}, {c: o.b}])
}
The mapping function will produce a pair of result items for each source item. They are regular, independent items of mapObjects: [ {c: 1}, {c: 2}, {c: 3}, {c: 4}, {c: 5}, {c: 6} ], but both resultant items are affected whenever corresponding source item is changed. Note, that we have to use a special return value here: ListCollectionMapResult. If the mapping function returns array it means that this array (and not its items) is the corresponding resultant item.
At this point the idea of ListCollectionMap is clear. The last example is about using of ListCollectionMap and ListCollectionView together. Lets take the ArrayCollection (which is also a ListCollectionView itself) with the initial integer sequence from 10 to one. Lets then wrap it with ListCollectionView that will filter out all even values, pass it to ListCollectionMap, that multiplies each value by 2 and at last add ListCollectionView that will sort values. E.g. in Perl it would sound like:
sort { $a <=> $b } map { $_ * 2 } grep { $_ % 2 } (10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
In AS3 it will be a bit longer:
public var dataIntegers:ArrayCollection
= new ArrayCollection([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
public var filteringView:ListCollectionView;
public var multMap:ListCollectionMap;
public var sortingView:ListCollectionView;
public function filterFunction(i:int):Boolean
{
return i % 2 ? true : false
}
public function mapFunction(i:int):int
{
return i * 2
}
filteringView = new ListCollectionView(dataIntegers)
filteringView.filterFunction = filterFunction
filteringView.refresh()
multMap = new ListCollectionMap(mapFunction, filteringView)
sortingView = new ListCollectionView(multMap)
sortingView.sort = new Sort()
sortingView.refresh()
Both perl and as3 code will produce the sequence (2, 6, 10, 14, 18) as a result. Again, the sortingView content is affected whenever something is added, deleted, updated or moved in dataIntegers collection.
Also note, that the magic of ListCollectionView and ListCollectionMap is not for free: each instance of these classes uses a lot of additional memory under the hood. The ListCollectionView stores the index of filtered or sorted objects while ListCollectionMap has the full index of source items inside to be able to keep track on changes.
The source code and library can be downloaded here.