2010-01-22 23 views
7

Ho letto ciò che ho trovato su Stackoverflow e non sono ancora chiaro su questo.Ordinamento di una matrice di oggetti SimpleXML

Ho un array di oggetti SimpleXML qualcosa di simile:

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

e voglio essere in grado di ordinare in qualsiasi colonna, crescente o decrescente. Qualcosa di simile:

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

Dove si può passare nella matrice sopra degli oggetti e ordina dal valore di qualsiasi tasto mi piace.

Per riferimento, una soluzione simile .NET sarebbe in questo senso: la gente

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

che ho visto dire

"Use usort"

Seguito da un esempio di base del manuale di PHP, ma questo doesn lo spiego davvero Al meno non a me. Ho anche visto persone suggerire l'uso di una libreria esterna come SimpleDOM ma voglio evitare di usare qualcosa di esterno per questo (apparentemente, anche se non riesco ancora a risolverlo) piccola cosa.

Qualsiasi aiuto è apprezzato, grazie!

risposta

3

Immagino che le persone che suggeriscono di usare SimpleDOM sarebbero io. :)

Ho scritto SimpleDOM :: sort() esattamente per quella situazione, perché per ordinare SimpleXMLElements tramite un'espressione di arbitraggio (o espressioni arbitrarie) è necessario utilizzare array_multisort() che è noioso e non ti insegnerà qualsiasi cosa utile

Ecco la versione breve di come funziona: innanzitutto si crea un array proxy di chiavi => coppie di valori corrispondenti a ciascun SimpleXMLElement e il valore con cui verranno ordinate. Nel tuo esempio, se vuoi ordinarli per <age/>, la matrice sarà array(21, 56). Quindi chiami array_multisort() con "array proxy" come primo argomento, seguito da un numero qualsiasi di sorting modifiers come SORT_DESC o SORT_NUMERIC e infine l'array che desideri ordinare, che verrà passato per riferimento.

Vi ritroverete con qualcosa di simile:

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

Ma in realtà, invece di gravare te stesso con più codice dovrete mantenere e possibilmente finendo riscrivere array_multisort() in userspace, si dovrebbe sfruttare le soluzioni esistenti . Non c'è nulla di interessante in un algoritmo/routine di ordinamento, il tuo tempo sarebbe meglio speso per qualcosa che non esiste già.

+0

Beh diamine Josh, sei persistente! Suppongo che farò un tentativo in quanto mi permetterebbe di passare alla prossima pietra d'inciampo e potrei probabilmente usarlo in altri punti di questo progetto. – Stuart

+0

Non sono presistente, solo coerente nel sostenere il riutilizzo del codice. Se lo snippet che ho postato sopra funziona per te, allora per niente significa che lo usi. O usi chiusure se preferisci, qualunque cosa funzioni per te. Come hai detto, il tuo obiettivo è trovare una soluzione che ti permetta di passare al prossimo ostacolo e non ti richiederà di guardare più tardi. –

+0

Quel "persistente" doveva essere gratuito. Sono venduto su SimpleDOM. Funziona e basta. Veloce. Grandi cose, Josh. – Stuart

2

Ero pronto a raccomandare usort() fino a quando mi sono reso conto che mi hai già battuto. Dal momento che gli esempi di codice non hanno fatto molto bene in passato, cercherò di spiegarlo semplicemente in inglese e spero che ciò ti porti nella giusta direzione.

Utilizzando usort(), si crea il proprio "algoritmo" generato dall'utente. La funzione usort() chiama la propria funzione di confronto per determinare in che modo ciascuno degli oggetti si relaziona tra loro. Quando scrivi la tua funzione di confronto, sarai passato in due oggetti all'interno dell'array. Con questi due oggetti, si restituisce un risultato che dice essenzialmente a usort() se il primo oggetto è LESS THAN, EQUAL TO o GREATER THAN il secondo oggetto. Puoi farlo restituendo -1, 0 o 1 (rispettivamente). Questo è tutto. Devi solo preoccuparti di come due oggetti si confrontano tra loro e le meccaniche di ordinamento effettive sono gestite da usort() per te.

Ok, ora per esempio semi-pratico:

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

Questo è più o meno l'esempio nel manuale PHP, ma si spera che ha senso nel contesto ora. Fammi sapere se non sono chiaro su qualcosa.

+0

l'unico problema è che ha bisogno di rendere le funzioni esplicite per ogni proprietà perché vuole essere in grado di ordinarli in base a qualsiasi valore di elemento xml possibile. Pertanto ha bisogno di trovare un intermediario che consenta di utilizzare una proprietà fornita e ordinare al momento della chiamata. – prodigitalson

+0

È possibile utilizzare una variabile globale per specificare quale colonna verrà utilizzata nel confronto (tenere a mente i tipi di dati per confrontare tra stringhe e numeri).In realtà non consiglierei mai a qualcuno di usare globals, quindi, idealmente, implementalo in una classe e usa una variabile membro per specificare la colonna. Non ho mai usato 'usort()' in questo modo, quindi non posso promettere che funzionerà. Avrei eseguito un test rapido se avessi avuto più tempo. –

+1

L'idea di avvolgerlo in una classe è l'intermediario ideale. Forse estendendo ArrayObject. – prodigitalson

4

La funzione usort permette di dirvi PHP

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

Non ha nulla a che fare con specificamente SimpleXML. È una funzione generica per l'ordinamento della raccolta dati dell'array integrato PHP.

È necessario scrivere una funzione, un metodo di istanza o un metodo statico per ordinare la matrice. Il secondo argomento di usort accetta uno PHP Callback, che è uno pseudo-tipo che consente di specificare quale funzione, metodo di istanza o metodo statico.

La funzione che scrivi accetta due argomenti.Questi saranno due valori diversi dall'array

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

È necessario scrivere questa funzione per restituire uno dei tre valori.

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

Quando si chiama usort, PHP verrà eseguito attraverso il vostro array, chiamando la funzione/metodo di ordinamento (in questo caso cmp più e più volte fino a quando l'array è ordinato. Nel tuo esempio, $ a e $ volontà b .. essere oggetti SimpleXML

1

Ecco un altro esempio di utilizzo usort() Questo consente di specificare la variabile oggetto e la direzione di ordinamento:

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

codice di prova;

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>'; 
+0

perché $ sort_func = function ($ obj_1, $ obj_2) usa ($ sort_field, $ sort_direction) mi dà un errore di sintassi? – sloga

2

soluzione Josh Davis' non sembra funzionare durante il caricamento di un file XML tramite:

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

ottengo il seguente errore:

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

Si riferisce a: array_multisort ($ sort_proxy, $ order, $ nodi);

+0

xpath è necessario quando si carica XML tramite simplexml_load_file(). Vedi http://stackoverflow.com/questions/3998722/how-to-sort-a-multi-dimensional-xml-file –

1

è un filo vecchio, ma qui è la mia soluzione che un uso per estrarre informazioni da un feed rss per Ordina per titolo

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