Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting an array of SimpleXML objects

I've read what I've found on Stackoverflow and am still unclear on this.

I have an array of SimpleXML objects something like this:

array(2) {
  [0]=>
  object(SimpleXMLElement)#2 (2) {
    ["name"]=>
    string(15) "Andrew"
    ["age"]=>
    string(2) "21"
  }
  [1]=>
  object(SimpleXMLElement)#3 (2) {
    ["name"]=>
    string(12) "Beth"
    ["age"]=>
    string(2) "56"
  }
}

And I want to be able to sort by whatever column, ascending or descending. Something like:

sort($data, 'name', 'asc');

Where I can pass in the above array of objects and sort by the value of whichever key I like.

For reference, a similar .NET solution would be along these lines:

XmlSortOrder order = XmlSortOrder.Ascending;
    if ( sortDirection == "asc" ) {
        order = XmlSortOrder.Ascending;
    }
    expression.AddSort( columnSortingOn + "/text()", order, 
        XmlCaseOrder.UpperFirst, "en-us", XmlDataType.Text ); 

I've seen people say

"Use usort"

Followed by a basic example from the PHP manual but this doesn't really explain it. At least not to me. I've also seen people suggest using an external library like SimpleDOM but I want to avoid using something external for this (seemingly, though I cannot yet solve it) little thing.

Any help is appreciated, Thanks!

like image 964
rg88 Avatar asked Jan 22 '10 18:01

rg88


1 Answers

I guess the people suggesting to use SimpleDOM would be me. :)

I've written SimpleDOM::sort() exactly for that situation, because in order to sort SimpleXMLElements by an arbitration expression (or arbitrary expressions) you need to use array_multisort() which is boring and won't teach you anything useful.

Here's the short version of how it works: first you create a proxy array of key=>value pairs corresponding to each SimpleXMLElement and the value with which they'll be sorted. In your example, if you want to sort them by <age/>, the array would be array(21, 56). Then you call array_multisort() with the "proxy array" as first argument, followed by any number of sorting modifiers such as SORT_DESC or SORT_NUMERIC then finally the array you want to sort, which will be passed by reference.

You will end up with something like that:

$nodes = array(
    new SimpleXMLElement('<person><name>Andrew</name><age>21</age></person>'),
    new SimpleXMLElement('<person><name>Beth</name><age>56</age></person>')
);

function xsort(&$nodes, $child_name, $order = SORT_ASC)
{
    $sort_proxy = array();

    foreach ($nodes as $k => $node)
    {
        $sort_proxy[$k] = (string) $node->$child_name;
    }

    array_multisort($sort_proxy, $order, $nodes);
}

xsort($nodes, 'name', SORT_ASC);
print_r($nodes);

xsort($nodes, 'age', SORT_DESC);
print_r($nodes);

But really, instead of burdening yourself with more code you'll have to maintain and possibly ending up rewriting array_multisort() in userspace, you should leverage existing solutions. There's nothing interesting in such a sorting algorithm/routine, your time would be better spent on something that doesn't already exist.

like image 166
Josh Davis Avatar answered Sep 23 '22 20:09

Josh Davis