Posortować tablicę według kluczy na podstawie innej tablicy?

153

Czy w PHP można zrobić coś takiego? Jak zabrałbyś się do napisania funkcji? Oto przykład. Porządek jest najważniejszy.

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

I chciałbym zrobić coś takiego

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

Ponieważ na końcu używam foreach () i nie są one we właściwej kolejności (ponieważ dołączam wartości do ciągu, który musi być w odpowiedniej kolejności i nie znam z góry wszystkich kluczy tablicy / wartości).

Przejrzałem wewnętrzne funkcje tablicowe PHP, ale wydaje się, że możesz sortować tylko alfabetycznie lub numerycznie.

Alex
źródło

Odpowiedzi:

347

Po prostu użyj array_mergelub array_replace. Array_mergedziała zaczynając od podanej tablicy (w odpowiedniej kolejności) i nadpisując / dodając klucze danymi z aktualnej tablicy:

$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 - odpowiadam na to „nieaktualne” pytanie, ponieważ uważam, że wszystkie pętle podane jako poprzednie odpowiedzi są przesadą.

Darkwaltz4
źródło
31
Działa dobrze, jeśli masz klucze łańcuchowe, ale nie numeryczne. Dokumenty PHP: "Jeśli tablice wejściowe mają te same klucze łańcuchowe, późniejsza wartość tego klucza nadpisze poprzednią. Jeśli jednak tablice zawierają klucze numeryczne, późniejsza wartość nie nadpisze oryginalnej wartości, ale zostanie dołączone. "
bolbol
7
Fajnie, ale co jeśli w wartościach nie ma kluczy? Potrzebuję tego, ale tylko jeśli którykolwiek z kluczy istnieje ... Prawdopodobnie potrzebuję na nim foreach ...
Solomon Closson
5
w moim przypadku jest to array_replace zamiast array_merge. array_merge łączy obie wartości zamiast zamieniać drugą tablicę na uporządkowane klucze.
neofreko
3
Na Twoje rozwiązanie natknąłem się kilka lat temu, szukając czegoś innego - i pomyślałem sobie, że jest to niezwykle wydajne w porównaniu z pętlami. Teraz potrzebuję twojego rozwiązania i znalezienie go zajęło mi godzinę! Dzięki!
Michael
4
Dodatkowo, jeśli tablica 'order' (tj. Tablica ('nazwa', 'dob', 'adres')) ma więcej kluczy niż tablica do sortowania, to dodatkowa tablica_przetnij wynikową posortowaną tablicę z oryginalną tablicą by się odcięła bezpańskie klucze, które zostały dodane w array_merge.
bbe
105

Proszę bardzo:

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
źródło
12
Więc możesz połączyć 2 tablice ze znakiem +? Nigdy tego nie wiedziałem, używałem array_merge()!
Alex
3
Czy to lepsze niż używanie usort()lub uasort()?
grantwparks
5
Po breakznalezieniu wartości należy wstawić instrukcję.
Adel
4
@alex Zachowaj ostrożność podczas zastępowania operatorem array_merge()tablicy +. Łączy według klucza (również dla klawiszy numerycznych) i od lewej do prawej , podczas gdy array_mergełączy się od prawej do lewej i nigdy nie zastępuje klawiszy numerycznych. Np [0,1,2]+[0,5,6,7] = [0,1,2,7]podczas array_merge([0,1,2],[0,5,6,7]) = [0,1,2,0,5,6,7]i ['a' => 5] + ['a' => 7] = ['a' => 5]ale array_merge(['a' => 5], ['a' => 7]) = ['a' => 7].
grypa
Czy używanie +znaku jest bezpieczne ?
crmpicco
47

A co z tym rozwiązaniem

$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
źródło
1
Ten wymaga więcej głosów, inne nie działały / nie działają dobrze, podczas gdy to działa dobrze w moim przypadku.
Ng Sek Long
To nie jest zbyt wydajne. Dla każdego porównania wykonywane są dwa wyszukiwania liniowe w tablicy. Jeśli przyjmiemy, że złożoność czasowa uksort () jest równa be O(n * log n), to ten algorytm działa O(n^2 * log(n)).
TheOperator
36

Inny sposób dla 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);

Wynik:

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

Działa dobrze z klawiszami ciągowymi i numerycznymi.

ekuser
źródło
2
+ Podczas gdy oboje pracują, okazało array_replace()się , że lepiej przekazują zamiary niż array_merge().
Jason McCreary
1
array_replacerównież pozostawia nienaruszony typ zmiennej. Jeśli jedna z wartości w twojej tablicy byłaby (string) '1'i +(int) 1
użyłbyś
1
Działa to również na klawiszach numerycznych ( array_merge()czy wystarczy je dołączyć?). Logika jest bardzo dobrze wyjaśnione tutaj . Po pierwsze , array_flip()zmienia wartości w $ order tablicę do kluczy. Po drugie , array_replace()zastępuje wartości z pierwszej tablicy wartościami z tymi samymi kluczami w drugiej tablicy. Jeśli chcesz posortować tablicę według kluczy z innej tablicy, nie musisz jej nawet używać 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
źródło
14

Wybierz jedną tablicę jako zamówienie:

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

Możesz posortować inną tablicę na podstawie wartości za pomocą array_intersectDokumentów :

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

Lub w Twoim przypadku, aby posortować według kluczy, użyj array_intersect_keyDokumentów :

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

Obie funkcje zachowają kolejność pierwszego parametru i zwrócą tylko wartości (lub klucze) z drugiej tablicy.

Tak więc w tych dwóch standardowych przypadkach nie musisz samodzielnie pisać funkcji, aby wykonać sortowanie / zmianę kolejności.

hakre
źródło
Skrzyżowanie pozbyłoby się wpisów, których wcześniej nie zna.
DanMan
1
Jest to nieprawidłowe w przypadku sortowania według kluczy. array_intersect_key zwróci tylko wartości z array1, a nie array2
upiorny
Uzgodniono z pavsid - przykład array_intersect_key jest niepoprawny - zwraca wartości z pierwszej tablicy, a nie drugiej.
Jonathan Aquino
10

Użyłem rozwiązania Darkwaltz4, ale array_fill_keyszamiast tego użyłem array_flip, aby wypełnić, NULLjeśli klucz nie jest ustawiony $array.

$properOrderedArray = array_replace(array_fill_keys($keys, null), $array);
Baptiste Bernard
źródło
5

Bez magii ...

$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
źródło
1
Działa to nieźle, wystarczy zaktualizować $datasetnazwę parametru
kursus
3

JEŚLI masz tablicę w swojej tablicy, będziesz musiał trochę dostosować funkcję przez Erana ...

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
źródło
2

Ta funkcja zwraca podrzędną i posortowaną tablicę na podstawie kluczy $ drugiego parametru

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

Przykład:

$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
źródło
1

PHP ma funkcje, które pomogą Ci w tym:

$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 wykonuje całą pracę za Ciebie, a array_search dostarcza klucze. array_search () zwraca false, gdy nie może znaleźć dopasowania, więc elementy, które nie znajdują się w tablicy sortowania, naturalnie przesuwają się na dół tablicy.

Uwaga: uasort () uporządkuje tablicę bez wpływu na relacje klucz => wartość.

danielcraigie
źródło
1
  • sortuj zgodnie z żądaniem
  • zapisz dla int-keys (z powodu array_replace)
  • Don't return klucze nie istnieją w inputArray
  • (opcjonalnie) klucze filtrów nie istnieją w podanej liście kluczy

Kod:

 /**
 * 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;
}
Ziarno
źródło
1

Pierwsza sugestia

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

Druga sugestia

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

Chciałem zaznaczyć, że obie te sugestie są niesamowite. Są to jednak jabłka i pomarańcze. Różnica? Jeden jest przyjazny dla asocjacji, a drugi jest przyjazny dla skojarzeń. Jeśli używasz 2 w pełni asocjacyjnych tablic, wówczas scalanie / odwracanie szyku w rzeczywistości scali i nadpisze inną tablicę asocjacyjną. W moim przypadku to nie są wyniki, których szukałem. Użyłem pliku settings.ini do utworzenia tablicy porządku sortowania. Tablica danych, którą sortowałem, nie musiała zostać nadpisana przez mojego odpowiednika do sortowania asocjacyjnego. Zatem scalenie tablic zniszczyłoby moją tablicę danych. Obie są świetnymi metodami, obie muszą być zarchiwizowane w dowolnym zestawie narzędzi programistów. W zależności od potrzeb może się okazać, że w swoich archiwach faktycznie potrzebujesz obu koncepcji.

user1653711
źródło
1

Przyjąłem odpowiedź z @ Darkwaltz4 ze względu na jej zwięzłość i chciałbym podzielić się, w jaki sposób dostosowałem rozwiązanie do sytuacji, w których tablica może zawierać różne klucze dla każdej iteracji, na przykład:

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

i utrzymywał „klucz główny” w następujący sposób:

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

array_merge wykonałby scalanie w iteracji Array [1] w oparciu o $ master_key i wyprodukował ['some_key'] = '', pustą wartość dla tej iteracji. Dlatego też array_intersect_key został użyty do zmodyfikowania $ master_key w każdej iteracji, tak jak poniżej:

foreach ($customer as $customer) {
  $modified_key = array_intersect_key($master_key, $unordered_array);
  $properOrderedArray = array_merge($modified_key, $customer);
}
Pageii Studio
źródło
0

Trochę późno, ale nie mogłem znaleźć sposobu, w jaki go zaimplementowałem, ta wersja wymaga zamknięcia, php> = 5.3, ale można ją zmienić, aby:

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

Oczywiście „dontSortMe” wymaga uporządkowania i może pojawić się jako pierwsze w przykładzie

DJules
źródło