Ordinare un array per chiavi in ​​base a un altro array?


153

In PHP è possibile fare qualcosa del genere? Come faresti a scrivere una funzione? Ecco un esempio L'ordine è la cosa più importante.

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

E mi piacerebbe fare qualcosa del genere

$properOrderedArray = sortArrayByArray($customer, array('name', 'dob', 'address'));

Perché alla fine uso un foreach () e non sono nell'ordine giusto (perché aggiungo i valori a una stringa che deve essere nell'ordine corretto e non conosco in anticipo tutte le chiavi dell'array / valori).

Ho esaminato le funzioni dell'array interno di PHP ma sembra che tu possa ordinare solo alfabeticamente o numericamente.

Risposte:


348

Basta usare array_mergeo array_replace. Array_mergefunziona iniziando con l'array che gli viene fornito (nell'ordine corretto) e sovrascrivendo / aggiungendo le chiavi con i dati dell'array reale:

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$properOrderedArray = array_merge(array_flip(array('name', 'dob', 'address')), $customer);
//Or:
$properOrderedArray = array_replace(array_flip(array('name', 'dob', 'address')), $customer);

//$properOrderedArray -> array('name' => 'Tim', 'address' => '123 fake st', 'dob' => '12/08/1986', 'dontSortMe' => 'this value doesnt need to be sorted')

ps - sto rispondendo a questa domanda "stantia", perché penso che tutti i loop forniti come risposte precedenti siano eccessivi.


31
Funziona bene se hai le chiavi di stringa ma non per quella numerica. PHP Docs: "Se gli array di input hanno le stesse chiavi di stringa, il valore successivo per quella chiave sovrascriverà quello precedente. Se, tuttavia, gli array contengono chiavi numeriche, il valore successivo non sovrascriverà il valore originale, ma sarà aggiunto."
bolbol

7
Bello, ma cosa succede se le chiavi non esistono nei valori? Ho bisogno di questo, ma solo se esiste una delle chiavi ... Probabilmente ho bisogno di una foreach su di esso allora ...
Solomon Closson

5
nel mio caso è array_replace invece di array_merge. array_merge combina entrambi i valori invece di sostituire il secondo array nelle chiavi ordinate.
Neofreko,

3
Mi sono imbattuto nella tua soluzione un paio di anni fa mentre cercavo qualcosa di diverso - e ho pensato a me stesso, questo è estremamente efficiente rispetto ai loop. Ora ho bisogno della tua soluzione e mi ci è voluta un'ora di ricerca per ritrovarla! Grazie!
Michael,

4
Inoltre, se l'array 'order' (cioè array ('name', 'dob', 'address')) ha più chiavi dell'array da ordinare, allora l'interruzione array_array aggiuntiva dell'array risultante con l'array originale verrebbe interrotta chiavi vaganti aggiunte a array_merge.
Be

105

Ecco qua:

function sortArrayByArray(array $array, array $orderArray) {
    $ordered = array();
    foreach ($orderArray as $key) {
        if (array_key_exists($key, $array)) {
            $ordered[$key] = $array[$key];
            unset($array[$key]);
        }
    }
    return $ordered + $array;
}

12
Quindi puoi unire 2 matrici con un segno +? Non l'ho mai saputo, lo sto usando array_merge()!
alex,

3
È meglio dell'uso usort()o uasort()?
grantwparks,

5
È necessario inserire breakun'istruzione una volta trovato il valore.
Adel,

4
@alex Fare molta attenzione quando si sostituisce array_merge()con l' +operatore dell'array . Si unisce per tasto (anche per i tasti numerici) e da sinistra a destra , mentre si array_mergeunisce da destra a sinistra e non sovrascrive mai i tasti numerici. Per esempio [0,1,2]+[0,5,6,7] = [0,1,2,7]mentre array_merge([0,1,2],[0,5,6,7]) = [0,1,2,0,5,6,7]e ['a' => 5] + ['a' => 7] = ['a' => 5]ma array_merge(['a' => 5], ['a' => 7]) = ['a' => 7].
influenza

È sicuro usare il +segno?
crmpicco,

47

Che ne dici di questa soluzione

$order = array(1,5,2,4,3,6);

$array = array(
    1 => 'one',
    2 => 'two',
    3 => 'three',
    4 => 'four',
    5 => 'five',
    6 => 'six'
);

uksort($array, function($key1, $key2) use ($order) {
    return (array_search($key1, $order) > array_search($key2, $order));
});

1
Questo ha bisogno di più voti, l'altro non ha funzionato / non ha funzionato bene mentre questo funziona bene nel mio caso.
Ng Sek Long,

Questo non è molto efficiente. Per ogni confronto, vengono eseguite due ricerche lineari nell'array. Se supponiamo che la complessità temporale di uksort () sia O(n * log n), allora questo algoritmo viene eseguito O(n^2 * log(n)).
Operatore il

36

Un altro modo per PHP> = 5.3.0:

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$customerSorted = array_replace(array_flip(array('name', 'dob', 'address')), $customer);

Risultato:

Array (
  [name] => Tim
  [dob] => 12/08/1986
  [address] => 123 fake st
  [dontSortMe] => this value doesnt need to be sorted
)

Funziona bene con i tasti stringa e numerici.


2
+ Mentre entrambi funzionano, ho scoperto array_replace()di trasmettere l'intento meglio di array_merge().
Jason McCreary,

1
array_replacelascia intatto anche il tipo di variabile. Se uno dei valori nel tuo array fosse stato (string) '1'e avessi usato l' +operatore, il valore sarebbe stato trasformato in(int) 1
halfpastfour.am

1
Questo funziona anche con i tasti numerici ( array_merge()li aggiungeresti semplicemente?). La logica è molto ben spiegata qui . Innanzitutto , array_flip()modifica i valori dell'array $ order in chiavi. In secondo luogo , array_replace()sostituisce i valori del primo array con valori con le stesse chiavi nel secondo array. Se è necessario ordinare un array in base alle chiavi di un altro, non è nemmeno necessario utilizzarlo array_flip.
aexl

23
function sortArrayByArray(array $toSort, array $sortByValuesAsKeys)
{
    $commonKeysInOrder = array_intersect_key(array_flip($sortByValuesAsKeys), $toSort);
    $commonKeysWithValue = array_intersect_key($toSort, $commonKeysInOrder);
    $sorted = array_merge($commonKeysInOrder, $commonKeysWithValue);
    return $sorted;
}

14

Prendi un array come tuo ordine:

$order = array('north', 'east', 'south', 'west');

Puoi ordinare un altro array in base ai valori utilizzando array_intersectDocumenti :

/* sort by value: */
$array = array('south', 'west', 'north');
$sorted = array_intersect($order, $array);
print_r($sorted);

O nel tuo caso, per ordinare in base alle chiavi, utilizza array_intersect_keyDocumenti :

/* sort by key: */
$array = array_flip($array);
$sorted = array_intersect_key(array_flip($order), $array);
print_r($sorted);

Entrambe le funzioni manterranno l'ordine del primo parametro e restituiranno solo i valori (o le chiavi) dal secondo array.

Quindi per questi due casi standard non è necessario scrivere una funzione per eseguire l'ordinamento / riordino.


L'intersezione si sbarazzerebbe di quelle voci che non conosceva in anticipo.
DanMan

1
Questo non è corretto per l'ordinamento per chiavi. array_intersect_key restituirà i valori solo da array1, non array2
spettrale il

Concordato con pavsid - l'esempio array_intersect_key non è corretto - restituisce i valori dal primo array, non dal secondo.
Jonathan Aquino,

10

Ho usato la soluzione di Darkwaltz4 ma ho usato array_fill_keysinvece di array_flip, per riempire NULLse non è stata inserita una chiave $array.

$properOrderedArray = array_replace(array_fill_keys($keys, null), $array);

5

Senza magia ...

$array=array(28=>c,4=>b,5=>a);
$seq=array(5,4,28);    
SortByKeyList($array,$seq) result: array(5=>a,4=>b,28=>c);

function sortByKeyList($array,$seq){
    $ret=array();
    if(empty($array) || empty($seq)) return false;
    foreach($seq as $key){$ret[$key]=$dataset[$key];}
    return $ret;
}

1
Funziona bene grazie, basta aggiornare $datasetper abbinare il nome del parametro
kursus

3

SE hai un array nel tuo array, dovrai adattare un po 'la funzione di Eran ...

function sortArrayByArray($array,$orderArray) {
    $ordered = array();
    foreach($orderArray as $key => $value) {
        if(array_key_exists($key,$array)) {
                $ordered[$key] = $array[$key];
                unset($array[$key]);
        }
    }
    return $ordered + $array;
}

2

Questa funzione restituisce un array secondario e ordinato in base al secondo parametro $ keys

function array_sub_sort(array $values, array $keys){
    $keys = array_flip($keys);
    return array_merge(array_intersect_key($keys, $values), array_intersect_key($values, $keys));
}

Esempio:

$array_complete = [
    'a' => 1,
    'c' => 3,
    'd' => 4,
    'e' => 5,
    'b' => 2
];

$array_sub_sorted = array_sub_sort($array_complete, ['a', 'b', 'c']);//return ['a' => 1, 'b' => 2, 'c' => 3];

1

PHP ha delle funzioni per aiutarti in questo:

$arrayToBeSorted = array('west', 'east', 'south', 'north');
$order = array('north', 'south', 'east', 'west');

// sort array
usort($arrayToBeSorted, function($a, $b) use ($order){
    // sort using the numeric index of the second array
    $valA = array_search($a, $order);
    $valB = array_search($b, $order);

    // move items that don't match to end
    if ($valA === false)
        return -1;
    if ($valB === false)
        return 0;

    if ($valA > $valB)
        return 1;
    if ($valA < $valB)
        return -1;
    return 0;
});

Usort fa tutto il lavoro per te e array_search fornisce le chiavi. array_search () restituisce false quando non riesce a trovare una corrispondenza, quindi gli elementi che non si trovano nella matrice di ordinamento si spostano naturalmente nella parte inferiore della matrice.

Nota: uasort () ordinerà l'array senza influenzare le relazioni chiave => valore.


1
  • ordina come richiesto
  • salva per int-keys (a causa di array_replace)
  • le chiavi non restituire non esistono in inputArray
  • (facoltativamente) chiavi filtro non esistenti in un determinato elenco chiavi

Codice:

 /**
 * sort keys like in key list
 * filter: remove keys are not listed in keyList
 * ['c'=>'red', 'd'=>'2016-12-29'] = sortAndFilterKeys(['d'=>'2016-12-29', 'c'=>'red', 'a'=>3 ]], ['c', 'd', 'z']){
 *
 * @param array $inputArray
 * @param string[]|int[] $keyList
 * @param bool $removeUnknownKeys
 * @return array
 */
static public function sortAndFilterKeys($inputArray, $keyList, $removeUnknownKeys=true){
    $keysAsKeys = array_flip($keyList);
    $result = array_replace($keysAsKeys, $inputArray); // result = sorted keys + values from input + 
    $result = array_intersect_key($result, $inputArray); // remove keys are not existing in inputArray 
    if( $removeUnknownKeys ){
        $result = array_intersect_key($result, $keysAsKeys); // remove keys are not existing in keyList 
    }
    return $result;
}

1

Primo suggerimento

function sortArrayByArray($array,$orderArray) {
    $ordered = array();
    foreach($orderArray as $key) {
        if(array_key_exists($key,$array)) {
            $ordered[$key] = $array[$key];
            unset($array[$key]);
        }
    }
    return $ordered + $array;
}

Secondo suggerimento

$properOrderedArray = array_merge(array_flip(array('name', 'dob', 'address')), $customer);

Volevo sottolineare che entrambi questi suggerimenti sono fantastici. Tuttavia, sono mele e arance. La differenza? Uno è amichevole non associativo e l'altro è amichevole associativo. Se si utilizzano 2 array completamente associativi, l'unione / flip dell'array effettivamente unirà e sovrascriverà l'altro array associativo. Nel mio caso non sono i risultati che stavo cercando. Ho usato un file settings.ini per creare il mio array di ordinamento. L'array di dati che stavo ordinando non aveva bisogno di essere sovrascritto dalla mia controparte di ordinamento associativa. Quindi l'unione dell'array distruggerebbe il mio array di dati. Entrambi sono ottimi metodi, entrambi devono essere archiviati in qualsiasi toolbox per sviluppatori. In base alle tue esigenze potresti scoprire di aver effettivamente bisogno di entrambi i concetti nei tuoi archivi.


1

Ho adottato la risposta di @ Darkwaltz4 per la sua brevità e vorrei condividere il modo in cui ho adattato la soluzione alle situazioni in cui l'array può contenere chiavi diverse per ogni iterazione in questo modo:

Array[0] ...
['dob'] = '12/08/1986';
['some_key'] = 'some value';

Array[1] ...
['dob'] = '12/08/1986';

Array[2] ...
['dob'] = '12/08/1986';
['some_key'] = 'some other value';

e mantenuto una "chiave master" in questo modo:

$master_key = array( 'dob' => ' ' ,  'some_key' => ' ' );

array_merge avrebbe eseguito l'unione nell'iterazione Array [1] basata su $ master_key e prodotto ['some_key'] = '', un valore vuoto, per quell'iterazione. Quindi, array_intersect_key è stato usato per modificare $ master_key in ciascuna iterazione in questo modo:

foreach ($customer as $customer) {
  $modified_key = array_intersect_key($master_key, $unordered_array);
  $properOrderedArray = array_merge($modified_key, $customer);
}

0

Un po 'in ritardo, ma non sono riuscito a trovare il modo in cui l'ho implementato, questa versione necessita di chiusura, php> = 5.3, ma potrebbe essere modificata per non:

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$order = array('name', 'dob', 'address');

$keys= array_flip($order);
uksort($customer, function($a, $b)use($keys){
    return $keys[$a] - $keys[$b];
});
print_r($customer);

Naturalmente 'dontSortMe' deve essere risolto e potrebbe apparire per primo nell'esempio

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.