test.xml:
<?xml version="1.0"?> <props> <prop> <state statename="Mississippi"> <info> <code>a1</code> <location>Jackson</location> </info> <info> <code>d2</code> <location>Gulfport</location> </info> <info> <code>g6</code> <location>Hattiesburg</location> </info> </state> <state statename="Texas"> <info> <code>i9</code> <location>Dallas</location> </info> <info> <code>a7</code> <location>Austin</location> </info> </state> <state statename="Maryland"> <info> <code>s5</code> <location>Mount Laurel</location> </info> <info> <code>f0</code> <location>Baltimore</location> </info> <info> <code>h4</code> <location>Annapolis</location> </info> </state> </prop> </props>
test.php
// start the sortCities function sortCities($a, $b){ return strcmp($a->location, $b->location); } // start the sortStates function sortStates($t1, $t2) { return strcmp($t1['statename'], $t2['statename']); } $props = simplexml_load_file('test.xml'); foreach ($props->prop as $prop) { $sortedStates = array(); foreach($prop->state as $states) { $sortedStates[] = $states; } usort($sortedStates, "sortStates"); // finish the sortStates /* --- */ echo '<pre>'."\n"; print_r($sortedStates); echo '</pre>'."\n"; /* --- */ foreach ($prop->children() as $stateattr) { // this doesn't do it //foreach($sortedStates as $hotel => @attributes){ // blargh! if(isset($stateattr->info)) { $statearr = $stateattr->attributes(); echo '<optgroup label="'.$statearr['statename'].'">'."\n"; $options = array(); foreach($stateattr->info as $info) { $options[] = $info; } usort($options, "sortCities"); // finish the sortCities foreach($options as $stateattr => $info){ echo '<option value="'.$info->code.'">'.$info->location.'</option>'."\n"; } echo '</optgroup>'."\n"; } else { //empty nodes don't do squat } } } ?>
Это массив, который:
print_r($sortedStates);
распечатывает:
Array ( [0] => SimpleXMLElement Object ( [@attributes] => Array ( [statename] => Maryland ) [info] => Array ( [0] => SimpleXMLElement Object ( [code] => s5 [location] => Mount Laurel ) [1] => SimpleXMLElement Object ( [code] => f0 [location] => Baltimore ) [2] => SimpleXMLElement Object ( [code] => h4 [location] => Annapolis ) ) ) [1] => SimpleXMLElement Object ( [@attributes] => Array ( [statename] => Mississippi ) [info] => Array ( [0] => SimpleXMLElement Object ( [code] => a1 [location] => Jackson ) [1] => SimpleXMLElement Object ( [code] => d2 [location] => Gulfport ) [2] => SimpleXMLElement Object ( [code] => g6 [location] => Hattiesburg ) ) ) [2] => SimpleXMLElement Object ( [@attributes] => Array ( [statename] => Texas ) [info] => Array ( [0] => SimpleXMLElement Object ( [code] => i9 [location] => Dallas ) [1] => SimpleXMLElement Object ( [code] => a7 [location] => Austin ) ) ) )
это:
// start the sortCities function sortCities($a, $b){ return strcmp($a->location, $b->location); }
плюс эта часть кода:
$options = array(); foreach($stateattr->info as $info) { $options[] = $info; } usort($options, "sortCities"); // finish the sortCities foreach($options as $stateattr => $info){ echo '<option value="'.$info->code.'">'.$info->location.'</option>'."\n"; }
выполняет прекрасную работу по сортировке по узлу «location» внутри каждой optgroup.
Вы можете видеть, что в массиве я могу сортировать его по атрибуту «statename». У меня возникают проблемы с эхом и объединением двух функций, чтобы он автоматически сортировал как штаты, так и города внутри и формировал нужные группы optgroups.
Я попытался скопировать строки для городов и изменить имена, названные несколькими способами безрезультатно.
Поэтому, используя структуру XML выше, я пытаюсь заставить ее выглядеть так:
<optgroup label="Maryland"> <option value="h4">Annapolis</option> <option value="f0">Baltimore</option> <option value="s5">Mount Laurel</option> </optgroup> <optgroup label="Mississippi"> <option value="d2">Gulfport</option> <option value="g6">Hattiesburg</option> <option value="a1">Jackson</option> </optgroup> <optgroup label="Texas"> <option value="a7">Austin</option> <option value="i9">Dallas</option> </optgroup>
Таким образом, независимо от того, какой порядок упорядочивают состояния в XML и как бы упорядочиваются местоположения в узле внутри состояний, они всегда упорядочивают по алфавиту при создании optgroup.
Artefacto-
последние 2 блока кода показывают функцию для сортировки узлов по имени (узлы местоположения).
// start the sortCities function sortCities($a, $b){ return strcmp($a->location, $b->location); } // start the sortStates function sortStates($t1, $t2) { return strcmp($t1['statename'], $t2['statename']); }
вторая функция сортирует по атрибуту (statename) в массиве, но, расчесывая две функции или, скорее, вложенные в них, так что состояния и города сортируются в алфавитном порядке, меня озадачило.
@Artefacto,
Спасибо за ответ. Кажется, имеет смысл, как он вложен. Проблема заключается в том, что ни один из моих серверов не запускает PHP 5.3. Таким образом, общие функции бросают ошибки. Я должен был упомянуть об этом, но не думал об этом. Они работают 5.2. Я пытаюсь вернуть скрипт обратно и застрял в секции.
<?php $doc = simplexml_load_file('test.xml'); $states = get_object_vars($doc->prop->children()); $states = $states["state"]; function sortStates($t1, $t2) { return strcmp($t1['statename'], $t2['statename']); }; usort($states, "sortStates"); /* this is just here for testing */ echo '<pre>'; print_r($states); echo '</pre>'; /* end testing */ /* array_walk($states, function (&$state) { $state = get_object_vars($state); array_walk($state["info"], function (&$el) { $el = get_object_vars($el); } ); usort($state["info"], function($a, $b) { return strcmp($a["location"], $b["location"]); } ); } ); */ ?>
Заключенный раздел, начинающийся с array_walk. Я не могу понять, как переписать «функцию (& $ state)» со следующей смертью.
Вы хотите отсортировать список SimpleXMLElement
s на основе некоторого значения атрибута или элемента. Обычно вы делаете это, поворачивая список в массив, а затем применяете сортировку по этому массиву.
Функция, полезная для PHP, – iterator_to_array
. Это делает задачу превратить что-то настолько волшебное в simplexml в более «фиксированный» массив.
Скажем, ваши элементы тура в $result->tour
. По умолчанию simplexml возвращает здесь итератор , то, что вы не можете сортировать из коробки. Давайте преобразуем его в массив:
$tours = iterator_to_array($result->tour, false);
Теперь переменная $tours
представляет собой массив, содержащий каждый элемент <tour>
в качестве SimplXMLElement
. Теперь вы можете отсортировать этот массив с помощью функции массива. Эти функции сортировки массива описаны в руководстве по PHP, и я обычно предлагаю, как я могу сортировать многомерный массив в php как хорошую отправную точку:
$tourDates = array_map( function($tour) {return trim($tour->next_bookable_date); }, $tours ); // sort $tours array by dates, highest to lowest $success = array_multisort($tourDates, SORT_DESC, $tours);
И это уже так. Весь код с первого взгляда:
$tours = iterator_to_array($result->tour, false); $tourDates = array_map( function($tour) {return trim($tour->next_bookable_date); }, $tours ); // sort $tours array by dates, highest to lowest $success = array_multisort($tourDates, SORT_DESC, $tours); foreach ($tours as $tour) { ... }
Мой подход состоял в том, чтобы преобразовать объекты SimpleXMLElement в массивы:
$doc = new SimpleXMLElement($xml); $states = get_object_vars($doc->prop->children()); $states = $states["state"]; usort($states, function($t1, $t2) { return strcmp($t1['statename'], $t2['statename']); }); array_walk($states, function (&$state) { $state = get_object_vars($state); array_walk($state["info"], function (&$el) { $el = get_object_vars($el); } ); usort($state["info"], function($a, $b) { return strcmp($a["location"], $b["location"]); } ); } );
изменение этого
foreach ($prop->children() as $stateattr)
к этому
foreach ($sortedStates as $stateattr)
сделал это.
мог бы поклясться, что я уже это пробовал.