Сортировка массива объектов SimpleXML - PullRequest
7 голосов
/ 22 января 2010

Я прочитал то, что нашел в Stackoverflow, и мне до сих пор неясно,

У меня есть массив объектов SimpleXML примерно так:

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"
  }
}

И я хочу иметь возможность сортировать по любому столбцу, по возрастанию или по убыванию. Что-то вроде:

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

Где я могу передать приведенный выше массив объектов и отсортировать по значению того ключа, который мне нравится.

Для справки, аналогичное решение .NET будет выглядеть следующим образом:

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

Я видел, как люди говорили

«Используйте usort»

Далее приведен базовый пример из руководства по PHP, но это на самом деле не объясняет этого. По крайней мере, не для меня. Я также видел, как люди предлагают использовать внешнюю библиотеку, такую ​​как SimpleDOM, но я хочу избегать использования чего-то внешнего для этой (казалось бы, хотя я пока не могу ее решить) мелочи.

Любая помощь приветствуется, спасибо!

Ответы [ 6 ]

4 голосов
/ 22 января 2010

Функция usort позволяет вам сказать PHP

Эй, ты! Сортируйте этот массив, который я вам даю, с помощью этой функции, которую я написал.

Это не имеет ничего общего с SimpleXML. Это универсальная функция для сортировки данных встроенного массива PHP.

Вам нужно написать функцию, метод экземпляра или статический метод для сортировки массива. Второй аргумент usort принимает PHP Callback , который является псевдотипом, который позволяет указать, какую функцию, метод экземпляра или статический метод.

Функция, которую вы пишете, будет принимать два аргумента. Это будут два разных значения из вашего массива

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

Вам нужно написать эту функцию для возврата одного из трех значений.

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

Когда вы вызываете usort, PHP будет проходить через ваш массив, вызывая вашу функцию / метод сортировки (в этом случае cmp снова и снова, пока массив не будет отсортирован. В вашем примере $ a и $ b будут SimpleXML объекты.

3 голосов
/ 23 января 2010

Я думаю, что люди, предлагающие использовать SimpleDOM , будут мной.:)

Я написал SimpleDOM :: sort () именно для этой ситуации, потому что для сортировки SimpleXMLElements по арбитражному выражению (или произвольным выражениям) необходимо использовать array_multisort() это скучно и не научит вас чему-нибудь полезному.

Вот краткая версия того, как это работает: сначала вы создаете прокси-массив пар ключ => значение, соответствующих каждому элементу SimpleXMLElement и значению, с которым онибуду сортировать.В вашем примере, если вы хотите отсортировать их по <age/>, массив будет array(21, 56).Затем вы вызываете array_multisort() с «массивом прокси» в качестве первого аргумента, за которым следует любое количество модификаторов сортировки , таких как SORT_DESC или SORT_NUMERIC, затем, наконец, массив, который вы хотите отсортировать, который будет передан по ссылке.

В итоге вы получите нечто подобное:

$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);

Но на самом деле, вместо того, чтобы обременять себя большим количеством кода, вам придется поддерживать и, возможно, в конечном итоге переписать array_multisort() в пользовательском пространстве., вы должны использовать существующие решения.В таком алгоритме / рутине сортировки нет ничего интересного, лучше потратить время на то, чего еще не существует.

2 голосов
/ 26 октября 2010

Решение Джоша Дэвиса, похоже, не работает при загрузке XML-файла через:

$nodes = simplexml_load_file('/home/usr/public_html/feeds/deadlines.php');

Я получаю следующую ошибку:

Warning: array_multisort() [function.array-multisort]: Argument #3 is expected to be an array or a sort flag in /home/usr/public_html/feeds/deadlines.php on line 8

Это относится к: array_multisort ($ sort_proxy, $ order, $ node);

2 голосов
/ 22 января 2010

Я был готов рекомендовать usort(), пока не понял, что вы уже победили меня. Поскольку примеры кода в прошлом не приносили особой пользы, я постараюсь просто объяснить это на простом английском языке, и, надеюсь, это укажет вам правильное направление.

Используя usort(), вы создаете свой собственный пользовательский «алгоритм». Функция usort() вызывает вашу собственную функцию сравнения, чтобы определить, как каждый из ваших объектов связан друг с другом. При написании функции сравнения вы получите два объекта в вашем массиве. С этими двумя объектами вы возвращаете результат, который по существу сообщает usort(), является ли первый объект LESS THAN, EQUAL TO или GREATER THAN вторым объектом. Вы делаете это, возвращая -1, 0 или 1 (соответственно). Вот и все. Вам нужно только позаботиться о том, как два объекта сравниваются друг с другом, и фактическая механика сортировки обрабатывается usort() для вас.

Хорошо, теперь для полупрактического примера:

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');

Это в значительной степени пример в руководстве по PHP, но, надеюсь, теперь это имеет смысл в контексте. Дайте мне знать, если мне что-то неясно.

1 голос
/ 18 декабря 2013

это старая ветка, но вот мое решение, которое используется для извлечения информации из RSS-ленты для сортировки по заголовку

$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); 
1 голос
/ 22 января 2010

Вот еще один пример использования usort(). Этот позволяет указать переменную объекта и направление сортировки:

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);
}

Тестовый код;

<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 '
';
...