Grouping using a function
A colleague using the ILOG Elixir Treemap with Flex 3 GroupingCollection class for an internal ILOG application was recently asking me how he can group the collection items based on something a bit more complex than just a field value.
Until that I never had a look at this and was always grouping on the value of a given field like the reporter field in the following code:
<ilog:TreeMap ...>
<ilog:dataProvider>
<mx:GroupingCollection source="{source}">
<mx:Grouping label="reporter">
<mx:GroupingField name="reporter"/>
</mx:Grouping>
</mx:GroupingCollection>
</ilog:dataProvider>
</ilog:TreeMap>
That works fine for grouping all items with “Chris” as reporter on one hand and all items with “Julian” as reporter on the other hand with a data provider such as:
<items> <item reporter="Chris"/> <item reporter="Julian"/> <item reporter="Chris"/> <item reporter="Julian"/> </items>
But the data provider of my colleague was a bit more complex. It looks like:
<titems>
<item reporter="Chris"/>
<item reporter="Chris"/>
<item reporter="Julian"/>
<item reporter="Julian">
<owner name="Peter"/>
</item>
<item reporter="Julian">
<owner name="Peter"/>
</item>
<item reporter="Chris">
<owner name="Julian"/>
</item>
<item reporter="Chris"/>
</items>
And my colleague wanted a smarter grouping. He wanted the grouping to happen on the owner name field value if it is available and if not available on the reporter field value. Clearly just using the name property on the GroupingField is not enough.
Not knowing the answer, I was pretty sure it would be possible to do it as most Flex components provide both the ability to be configured based on field names but also using custom functions. Actually, it ended up being feasible but not as simple as I would have imagined, so let’s see how to proceed.
Looking at the asdoc of the class, the solution seems pretty simple, instead of using a name for the GroupingField the first thing to do is to set it up with a function instead:
<ilog:TreeMap ...>
<ilog:dataProvider>
<mx:GroupingCollection source="{source}">
<mx:Grouping label="reporter">
<mx:GroupingField groupingFunction="groupingFunction"/>
</mx:Grouping>
</mx:GroupingCollection>
</ilog:dataProvider>
</ilog:TreeMap>
Then just write the grouping function taking into account the requirement of looking first at the owner and then at the reporter as a fallback:
private function groupingFunction(item:Object,
field:GroupingField):String {
if (item.owner)
return item.owner.name;
// not found use reporter
return item.reporter;
}
Doing seems seems to work… well it is actually grouping according to the criteria, but I ended up with several “Chris” groups and several “Julian” groups. Looking at the Adobe source code, I realized that the grouping is relying on the fact that the data items must first be sorted according to the grouping criteria. GroupingCollection is doing that for you as soon as you are just relying on the name of the GroupingField, but if you do something tricky in the groupingFunction such as I did, then you have to help GroupingCollection do the sorting and provide the counterpart compare function to your grouping function. You can use it like that:
<ilog:TreeMap ...>
<ilog:dataProvider>
<mx:GroupingCollection source="{source}">
<mx:Grouping label="reporter">
<mx:GroupingField groupingFunction="groupingFunction"
compareFunction="compareFunction"/>
</mx:Grouping>
</mx:GroupingCollection>
</ilog:dataProvider>
</ilog:TreeMap>
And implement it like this:
private function compareFunction(item1:Object, item2:Object):int {
var v1:String = "";
var v2:String = "";
if (item1.owner) {
v1 = item1.owner.name;
} else {
v1 = item1.reporter;
}
if (item2.owner) {
v2 = item2.owner.name;
} else {
v2 = item2.reporter;
}
return ObjectUtil.stringCompare(v1, v2);
}
So you can see, that the solution exists, but is unfortunately not as simple as you can expect. Maybe GroupingCollection should provide an alternate mode where the grouping can be performed without requiring a pre-sorting. Maybe it would decrease performances, but at least in small datasets that would avoid to wonder why your grouping function is not sufficient to do the trick
You can apply that technique not only the ILOG Elixir Treemap but to any Flex component that is accepting GroupingCollection as data provider, such as the Flex AdvancedDataGrid component or other ILOG Elixir components such as the Gantt Chart.
Tags: GroupingCollection








April 10th, 2008 at 9:40 am
Interesting article! I was actually wondering whether you support some kind of grouping for the 3D charts. In some applications the ‘X-axis’ has some kind of ‘+’ signs to expand certain columns. Do you have a similar solution?
April 11th, 2008 at 6:11 am
Hi Tom,
Thanks! I’m not 100% sure of what you are looking for in term of grouping with 3D charts, so it is a bit hard to give you a definite answer. However we do not have a built-in support for grouping in 3D charts (yet). Don’t hesitate to share more details (or screenshot?) on our forums if you would like us to consider it for future release (or workaround to make it work with current release).
September 9th, 2008 at 3:08 pm
many thanks for that, i had the same issue with multiple groups of the same name having provided a group function, and i’m not sure i’d have figured out to provide a compare function.
i have to say though that Flex sucks when it comes to anything like this. the documentation should state that when one function is provided the other should be too. Another issue here is that the grouping function signature provides the GroupingField object thus allowing you to access which column is being grouped, whereas the comparefunction doesn’t.