Home » Php » php – Sorting an array of SimpleXML objects

php – Sorting an array of SimpleXML objects

Posted by: admin July 12, 2020 Leave a comment

Questions:

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!

How to&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.

Answer:

The usort function allows to you tell PHP

Hey, you! Sort this array I’m giving you with this function I wrote.

It has nothing specifically to do with SimpleXML. It’s a generic function for sorting PHP built-in array data collection.

You need to write a function, instance method, or static method to sort the array. The second argument to usort accepts a PHP Callback, which is a pseudo-type that lets you specify which function, instance method, or static method.

The function you write will accept two arguments. These will be two different values from your array

function cmp($a, $b)
{
            if ($a == $b) {
                return 0;
            }
            if($a < $b) {
                return -1;
            }
            if($a > $b) {
                return 1;
            }
}

You need to write this function to return one of three values.

If $a == $b, return 0
If $a > $b, return -1
if $a > $v, return 1  

When you call usort, PHP will run through your array, calling your sorting function/method (in this case cmp over and over again until the array is sorted. In your example, $a and $b will be SimpleXML objects.

Answer:

I was ready to recommend usort() until I realized you already beat me to it. Since code examples haven’t done much good in the past, I’ll try to just explain it in plain English and hopefully that’ll get you pointed in the right direction.

By using usort(), you create your own user-generated “algorithm”. The usort() function calls your own comparison function to determine how each of your objects relates to one another. When writing your comparison function, you will get passed in two objects within your array. With those two objects, you return a result that essentially tells usort() whether the first object is LESS THAN, EQUAL TO, or GREATER THAN the second object. You do this by returning -1, 0, or 1 (respectively). That’s it. You only have to concern yourself with how two objects compare to each other and the actual sorting mechanics are handled by usort() for you.

Ok, now for a semi-practical example:

function myCompare($obj1, $obj2) {
    if($obj1->someInt == $obj2->someInt) {
        return 0; // equal to
    } else if($obj1->someInt < $obj2->someInt) {
        return -1; // less than
    } else {
        return 1; // greater than
    }
}

$myArray = {a collection of your objects};

usort($myArray, 'myCompare');

This is pretty much the example in the PHP Manual, but hopefully it makes sense in context now. Let me know if I’m not being clear on something.

Answer:

Here’s another example of using usort(). This one allows you to specify the object variable and the sort direction:

function sort_obj_arr(& $arr, $sort_field, $sort_direction)
{
    $sort_func = function($obj_1, $obj_2) use ($sort_field, $sort_direction)
    {
        if ($sort_direction == SORT_ASC) {
            return strnatcasecmp($obj_1->$sort_field, $obj_2->$sort_field);
        } else {
            return strnatcasecmp($obj_2->$sort_field, $obj_1->$sort_field);
        }
    };
    usort($arr, $sort_func);
}

Test code;

class TestClass
{
    public $name;
    public $age;

    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age  = $age;
    }
}

$test[] = new TestClass('Tom', 28);
$test[] = new TestClass('Mary', 48);
$test[] = new TestClass('Beth', 38);
$test[] = new TestClass('Cindy', 18);
$test[] = new TestClass('Sid', 58);
$test[] = new TestClass('Mandy', 8);

$field = 'age';
$direction = SORT_DESC;
sort_obj_arr($test, $field, $direction);

echo '<pre>';
print_r($test);
echo '</pre>';

Answer:

it’s a old thread but here is my solution that a use to extract information from a rss feed in order to sort by title

$xml =  simplexml_load_file('rss.xml');
$msg = array();
$msg_count = $xml->channel->item->count();

for ($x=0;$x<$msg_count;$x++){
    $msg[$x]['titl'] = (string)$xml->channel->item[$x]->title;
    $msg[$x]['link'] = (string)$xml->channel->item[$x]->link;
    $msg[$x]['date'] = (string)$xml->channel->item[$x]->pubDate;
    $msg[$x]['time'] = strtotime(substr((string)$xml->channel->item[$x]->pubDate,4));
    $msg[$x]['desc'] = (string)$xml->channel->item[$x]->description;
    $msg[$x]['auth'] = (string)$xml->channel->item[$x]->author;
}

foreach ($msg as $key => $row) {
    $titl[$key] = $row['titl'];
    $link[$key] = $row['link'];
    $pdat[$key] = $row['date'];
    $time[$key] = $row['time'];
    $cate[$key] = $row['cate'];
    $desc[$key] = $row['desc'];
    $auth[$key] = $row['auth'];
}

array_multisort(
//change $titl to any variable created by "foreach"
    $titl, SORT_ASC,SORT_NATURAL | SORT_FLAG_CASE,
    $msg);