Ein Array nach Schlüsseln sortieren, basierend auf einem anderen Array?

153

Ist es in PHP möglich, so etwas zu tun? Wie würden Sie eine Funktion schreiben? Hier ist ein Beispiel. Die Reihenfolge ist das Wichtigste.

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

Und ich würde gerne so etwas machen

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

Weil ich am Ende ein foreach () verwende und sie nicht in der richtigen Reihenfolge sind (weil ich die Werte an eine Zeichenfolge anhänge, die in der richtigen Reihenfolge sein muss und ich nicht alle Array-Schlüssel im Voraus kenne / Werte).

Ich habe die internen Array-Funktionen von PHP durchgesehen, aber anscheinend können Sie nur alphabetisch oder numerisch sortieren.

Alex
quelle

Antworten:

346

Verwenden Sie einfach array_mergeoder array_replace. Array_mergeBeginnen Sie mit dem Array, das Sie ihm gegeben haben (in der richtigen Reihenfolge), und überschreiben / fügen Sie die Schlüssel mit Daten aus Ihrem tatsächlichen Array hinzu:

$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 - Ich beantworte diese "abgestandene" Frage, weil ich denke, dass alle Schleifen, die als vorherige Antworten angegeben wurden, übertrieben sind.

Darkwaltz4
quelle
31
Es funktioniert gut, wenn Sie Zeichenfolgenschlüssel haben, aber nicht für die numerische. PHP Docs: "Wenn die Eingabearrays dieselben Zeichenfolgenschlüssel haben, überschreibt der spätere Wert für diesen Schlüssel den vorherigen. Wenn die Arrays jedoch numerische Schlüssel enthalten, überschreibt der spätere Wert nicht den ursprünglichen Wert, sondern wird angehängt. "
Bolbol
7
Schön, aber was ist, wenn die Schlüssel in den Werten nicht vorhanden sind? Ich brauche das, aber nur, wenn einer der Schlüssel existiert ... Dann brauche ich wahrscheinlich einen Foreach ...
Solomon Closson
5
In meinem Fall ist es array_replace anstelle von array_merge. array_merge kombiniert beide Werte, anstatt das zweite Array durch die geordneten Schlüssel zu ersetzen.
Neofreko
3
Ich bin vor ein paar Jahren auf Ihre Lösung gestoßen, als ich nach etwas anderem gesucht habe - und ich dachte mir, das ist im Vergleich zu den Schleifen äußerst effizient. Jetzt brauche ich Ihre Lösung und habe eine Stunde lang gesucht, um sie wieder zu finden! Vielen Dank!
Michael
4
Wenn das Array 'order' (dh das Array ('name', 'dob', 'address')) mehr Schlüssel als das zu sortierende Array enthält, wird zusätzlich array_intersect des resultierenden sortierten Arrays mit dem ursprünglichen Array abgeschnitten Streuschlüssel, die bei array_merge hinzugefügt wurden.
bbe
105

Los geht's:

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;
}
Eran Galperin
quelle
12
Sie können also 2 Arrays mit einem + -Zeichen verbinden? Das habe ich nie gewusst, ich habe es benutzt array_merge()!
alex
3
Ist das besser als usort()oder uasort()?
Grantwparks
5
Sie sollten eine breakAnweisung einfügen , sobald der Wert gefunden wurde.
Adel
4
@alex Seien Sie sehr vorsichtig, wenn Sie durch array_merge()den Array- +Operator ersetzen . Es wird nach Schlüssel (auch für Zifferntasten) und von links nach rechts zusammengeführt , während array_mergees von rechts nach links zusammengeführt wird und Zifferntasten niemals überschreibt. ZB [0,1,2]+[0,5,6,7] = [0,1,2,7]während array_merge([0,1,2],[0,5,6,7]) = [0,1,2,0,5,6,7]und ['a' => 5] + ['a' => 7] = ['a' => 5]doch array_merge(['a' => 5], ['a' => 7]) = ['a' => 7].
Grippe
Ist es sicher, das +Schild zu benutzen ?
Crmpicco
47

Wie wäre es mit dieser Lösung

$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));
});
Peter de Groot
quelle
1
Dieser braucht mehr Upvotes, andere haben nicht funktioniert / funktionieren nicht gut, während dies in meinem Fall gut läuft.
Ng Sek Long
Das ist nicht sehr effizient. Für jeden Vergleich werden zwei lineare Suchen im Array durchgeführt. Wenn wir die zeitliche Komplexität von uksort () annehmen O(n * log n), läuft dieser Algorithmus in O(n^2 * log(n)).
TheOperator
36

Ein anderer Weg für 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);

Ergebnis:

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

Funktioniert gut mit Zeichenfolgen und Zifferntasten.

ekuser
quelle
2
+ Während beide arbeiten, habe ich festgestellt, dass ich array_replace()Absichten besser weitergeben kann als array_merge().
Jason McCreary
1
array_replaceLässt auch den Variablentyp intakt. Wenn einer der Werte in Ihrem Array gewesen wäre (string) '1'und Sie den +Operator verwendet hätten, wäre der Wert in(int) 1
halfpastfour.am
1
Dies funktioniert auch bei Zifferntasten ( array_merge()würden sie nur angehängt?). Die Logik ist sehr gut erklärt hier . Erstens , array_flip()ändert den $ Werte Taste Befehl - Array. Zweitens werden array_replace()die Werte des ersten Arrays durch Werte mit denselben Schlüsseln im zweiten Array ersetzt. Wenn Sie ein Array nach Schlüsseln eines anderen sortieren müssen, müssen Sie es nicht einmal verwenden 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;
}
OIS
quelle
14

Nehmen Sie ein Array als Ihre Bestellung:

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

Sie können ein anderes Array anhand von Werten mithilfe von array_intersectDokumenten sortieren :

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

Oder verwenden Sie in Ihrem Fall zum Sortieren nach Schlüsseln die folgenden array_intersect_keyDokumente :

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

Beide Funktionen behalten die Reihenfolge des ersten Parameters bei und geben nur die Werte (oder Schlüssel) des zweiten Arrays zurück.

Für diese beiden Standardfälle müssen Sie also keine eigene Funktion schreiben, um das Sortieren / Neuanordnen durchzuführen.

hakre
quelle
Die Kreuzung würde die Einträge entfernen, die er nicht im Voraus kennt.
DanMan
1
Dies ist beim Sortieren nach Schlüsseln falsch. array_intersect_key nur geben die Werte von array1, nicht array2
spooky
Mit pavsid einverstanden - das Beispiel array_intersect_key ist falsch - gibt die Werte des ersten Arrays zurück, nicht des zweiten.
Jonathan Aquino
10

Ich habe die Lösung von Darkwaltz4 verwendet, aber array_fill_keysstattdessen verwendet array_flip, um zu füllen, NULLwenn kein Schlüssel eingegeben wurde $array.

$properOrderedArray = array_replace(array_fill_keys($keys, null), $array);
Baptiste Bernard
quelle
5

Ohne Magie ...

$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;
}
Jenovai Matyas
quelle
1
Das funktioniert gut, danke, aktualisiere einfach, um $datasetmit dem Parameternamen
übereinzustimmen
3

Wenn Sie ein Array in Ihrem Array haben, müssen Sie die Funktion von Eran ein wenig anpassen ...

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;
}
Boombastic
quelle
2

Diese Funktion gibt ein untergeordnetes und sortiertes Array zurück, das auf dem zweiten Parameter $ keys basiert

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

Beispiel:

$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];
Doglas
quelle
1

PHP hat Funktionen, die Ihnen dabei helfen:

$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 erledigt die ganze Arbeit für Sie und array_search stellt die Schlüssel bereit. array_search () gibt false zurück, wenn keine Übereinstimmung gefunden werden kann, sodass Elemente, die sich nicht im Sortierarray befinden, natürlich an den unteren Rand des Arrays verschoben werden.

Hinweis: uasort () ordnet das Array, ohne die Schlüssel => Wertebeziehungen zu beeinflussen.

danielcraigie
quelle
1
  • wie gewünscht sortieren
  • Speichern Sie für Int-Keys (wegen array_replace)
  • Keine Rückgabeschlüssel sind in inputArray nicht vorhanden
  • (optional) Filterschlüssel, die in der angegebenen Schlüsselliste nicht vorhanden sind

Code:

 /**
 * 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;
}
Korn
quelle
1

Erster Vorschlag

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

Zweiter Vorschlag

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

Ich wollte darauf hinweisen, dass diese beiden Vorschläge großartig sind. Es sind jedoch Äpfel und Orangen. Der Unterschied? Einer ist nicht assoziativ freundlich und der andere ist assoziativ freundlich. Wenn Sie 2 vollständig assoziative Arrays verwenden, wird das Zusammenführen / Umdrehen des Arrays tatsächlich zusammengeführt und das andere assoziative Array überschrieben. In meinem Fall sind das nicht die Ergebnisse, nach denen ich gesucht habe. Ich habe eine settings.ini-Datei verwendet, um mein Sortierreihenfolge-Array zu erstellen. Das Datenarray, das ich sortierte, musste nicht von meinem Gegenstück zur assoziativen Sortierung überschrieben werden. Somit würde das Zusammenführen von Arrays mein Datenarray zerstören. Beide sind großartige Methoden, beide müssen in jeder Entwickler-Toolbox archiviert werden. Je nach Ihren Anforderungen benötigen Sie möglicherweise beide Konzepte in Ihren Archiven.

user1653711
quelle
1

Ich habe die Antwort von @ Darkwaltz4 wegen ihrer Kürze übernommen und möchte mitteilen, wie ich die Lösung an Situationen angepasst habe, in denen das Array für jede Iteration unterschiedliche Schlüssel enthalten kann:

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

und pflegte einen "Hauptschlüssel" wie folgt:

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

array_merge hätte die Zusammenführung in der Array [1] -Iteration basierend auf $ master_key ausgeführt und für diese Iteration ['some_key'] = '', einen leeren Wert, erzeugt. Daher wurde array_intersect_key verwendet, um $ master_key in jeder Iteration wie folgt zu ändern:

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

Ein bisschen spät, aber ich konnte nicht finden, wie ich es implementiert habe. Diese Version muss geschlossen werden, php> = 5.3, könnte aber geändert werden, um nicht:

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

Natürlich muss 'dontSortMe' aussortiert werden und erscheint möglicherweise zuerst im Beispiel

DJules
quelle