22 July 2008

Sort XML by Attribute in Actionscript 3

I love working with E4X, but I wish there was an easy way to sort an XML based on an attribute.

After looking around I couldn't really find a good solution or maybe people weren't explaining themselves correctly, or I wasn't googling correctly. So I decided just to write my own small utility class.

When I thought about it. Arrays provide a superb sortOn() function, and it's relatively easy to leverage that.

code

I created a sortXMLByAttribute() public static function in my XMLUtils Class

public static function sortXMLByAttribute
 	(
		$xml		:	XML,
		$attribute	:	String,
		$options	:	Object	=	null,
		$copy		:	Boolean	=	false
	)
	:XML
 {
	//store in array to sort on
	var xmlArray:Array	= new Array();
	var item:XML;
	for each(item in $xml.children())
	{
		var object:Object = {
			data	: item, 
			order	: item.attribute($attribute)
		};
		xmlArray.push(object);
	}

	//sort using the power of Array.sortOn()
	xmlArray.sortOn('order',$options);

	//create a new XMLList with sorted XML
	var sortedXmlList:XMLList = new XMLList();
	var xmlObject:Object;
	for each(xmlObject in xmlArray )
	{
		sortedXmlList += xmlObject.data;
	}
	
	if($copy)
	{
		//don't modify original
		return	$xml.copy().setChildren(sortedXmlList);
	}
	else
	{
		//original modified
		return $xml.setChildren(sortedXmlList);
	}
 }

say what?

how to use

$xml : The XML that needs to be sorted
$attribute : The attribute string to sort on
$options : The sorting options, see sortOn()
$copy : If false, original XML is modified.

Here is an example, note this is in a package called XMLUtils

var xml:XML =
<body id="someId">
        <p displayOrder="15">Hello</p>
        <p displayOrder="7">World</p>
        <p displayOrder="3">Is</p>
        <p displayOrder="9">This</p>
        <p displayOrder="25">Thing</p>
        <p displayOrder="13">Working</p>
</body>

// original XML object
trace("BEFORE:" + xml);

XMLUtils.sortXMLByAttribute(
	xml,
	'displayOrder'
);
trace("After:" + xml);

XMLUtils.sortXMLByAttribute(
	xml,
	'displayOrder', 
	Array.NUMERIC
);
trace("Array.NUMERIC:" + xml");

//multiple options not don't modify orginal
var reverseXML:XML = XMLUtils.sortXMLByAttribute(
	xml,
	'displayOrder', 
	Array.NUMERIC | Array.DESCENDING, 
	true
);
trace("Array.NUMERIC | Array.DESCENDING:" + reverseXML);

source

Source code (in package+ + example) xmlutil.zip