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.

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ą.


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

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

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.


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

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.


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

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

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

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

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ść.


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

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.


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

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.