Jak mogę sortować tablice i dane w PHP?

292

To pytanie jest przeznaczone jako odniesienie do pytań dotyczących sortowania tablic w PHP. Łatwo jest myśleć, że Twój konkretny przypadek jest wyjątkowy i warty nowego pytania, ale w większości są to niewielkie odmiany jednego z rozwiązań na tej stronie.

Jeśli Twoje pytanie zostało zamknięte jako duplikat tego pytania, poproś o ponowne otwarcie pytania tylko wtedy, gdy możesz wyjaśnić, dlaczego różni się on wyraźnie od wszystkich poniższych.

Jak posortować tablicę w PHP?
Jak posortować złożoną tablicę w PHP?
Jak posortować tablicę obiektów w PHP?


  1. Podstawowe tablice jednowymiarowe; W tym Tablice wielowymiarowe, w tym. tablice obiektów; W tym Sortowanie jednej tablicy na podstawie innej

  2. Sortowanie za pomocą SPL

  3. Rodzaj stabilny

Aby uzyskać praktyczną odpowiedź przy użyciu istniejących funkcji PHP, patrz 1., szczegółową odpowiedź akademicką na temat algorytmów sortowania (które funkcje PHP implementują i które mogą być potrzebne w naprawdę, naprawdę skomplikowanych przypadkach), patrz 2.

deceze
źródło
@ jterry Dokładnie dlatego właśnie to zrobiłem, aby w końcu mieć dobre pytanie referencyjne do zamknięcia. Indywidualne odpowiadanie na każdy niepowtarzalny płatek śniegu nikomu nie pomaga. :)
deceze
3
Myślę, że ludzie powinni po prostu rzucić okiem na php.net
Alexander Jardim
@Alex Ha! Absolutnie. Problemem jest: nikt RTFM. : D
deceze
2
Mamy już te odpowiedzi. Sugeruję, abyś umieścił tutaj link do najlepszych odpowiedzi, zamiast powielać (lub przepisywać) treść. Również tablice są zazwyczaj postrzegane indywidualnie, więc w każdym razie praca musi być zamknięta w głosowaniu przeciwko duplikatom.
hakre
1
@deceze: Jeśli nikt nie będzie RTFM, nikt również nie będzie RTFQA - istniejące pytania i odpowiedzi :)
hakre

Odpowiedzi:

164

Podstawowe tablice jednowymiarowe

$array = array(3, 5, 2, 8);

Obowiązujące funkcje sortowania:

  • sort
  • rsort
  • asort
  • arsort
  • natsort
  • natcasesort
  • ksort
  • krsort

Różnica między nimi polega jedynie na tym, czy zachowane są powiązania klucz-wartość (funkcje „ a”), czy sortuje od niskiej do wysokiej, czy odwrotnie („ r”), czy sortuje wartości lub klucze („ k”) i jak porównuje wartości („ nat” vs. normalny). Zobacz http://php.net/manual/en/array.sorting.php aby uzyskać przegląd i łącza do dalszych szczegółów.

Tablice wielowymiarowe, w tym tablice obiektów

$array = array(
    array('foo' => 'bar', 'baz' => 42),
    array('foo' => ...,   'baz' => ...),
    ...
);

Jeśli chcesz sortować $arraywedług klucza „foo” każdego wpisu, potrzebujesz niestandardowej funkcji porównania . Powyższe sorti powiązane funkcje działają na prostych wartościach, które potrafią porównywać i sortować. PHP nie tylko „wie”, co zrobić ze złożoną wartością, taką jakarray('foo' => 'bar', 'baz' => 42) chociaż; więc musisz to powiedzieć.

Aby to zrobić, musisz utworzyć funkcję porównania . Ta funkcja wymaga dwóch elementów i musi zostać zwrócona, 0jeśli zostaną one uznane za równe, wartość niższa niż0 przypadku, gdy pierwsza wartość jest niższa, a wartość wyższa niż w 0przypadku, gdy pierwsza wartość jest wyższa. To wszystko, czego potrzeba:

function cmp(array $a, array $b) {
    if ($a['foo'] < $b['foo']) {
        return -1;
    } else if ($a['foo'] > $b['foo']) {
        return 1;
    } else {
        return 0;
    }
}

Często będziesz chciał użyć funkcji anonimowej jako oddzwaniania. Jeśli chcesz użyć metody lub metody statycznej, zapoznaj się z innymi sposobami określania wywołania zwrotnego w PHP .

Następnie użyj jednej z następujących funkcji:

Ponownie różnią się tylko tym, czy zachowują powiązania klucz-wartość i sortują według wartości lub kluczy. Przeczytaj ich dokumentację, aby uzyskać szczegółowe informacje.

Przykładowe użycie:

usort($array, 'cmp');

usortweźmie dwa elementy z tablicy i wywoła cmpz nimi twoją funkcję. Tak cmp()będzie nazywany $ajako array('foo' => 'bar', 'baz' => 42)i $bjak inny array('foo' => ..., 'baz' => ...). Funkcja zwraca następnie, usortktóra z wartości była większa lub czy były równe. usortpowtarza ten proces, przekazując różne wartości dla $ai $bdo momentu posortowania tablicy. cmpFunkcja zostanie wywołana wiele razy, co najmniej tyle razy, ile istnieją wartości w$array , z różnymi kombinacjami wartości dla $ai $bza każdym razem.

Aby przyzwyczaić się do tego pomysłu, spróbuj tego:

function cmp($a, $b) {
    echo 'cmp called with $a:', PHP_EOL;
    var_dump($a);
    echo 'and $b:', PHP_EOL;
    var_dump($b);
}

Wszystko, co zrobiłeś, to zdefiniowanie niestandardowego sposobu porównywania dwóch elementów, to wszystko, czego potrzebujesz. Działa to z wszelkiego rodzaju wartościami.

Nawiasem mówiąc, działa to na dowolną wartość, wartości nie muszą być złożonymi tablicami. Jeśli masz niestandardowe porównanie, które chcesz wykonać, możesz to zrobić również na prostej tablicy liczb.

sort sortuje według referencji i nie zwraca niczego przydatnego!

Zauważ, że tablica sortuje się na miejscu , nie musisz przypisywać wartości zwracanej do niczego. $array = sort($array)zastąpi tablicę tablicą true, a nie tablicą posortowaną. Po prostu sort($array);działa.

Niestandardowe porównania numeryczne

Jeśli chcesz sortować według bazklucza, który jest numeryczny, wszystko, co musisz zrobić, to:

function cmp(array $a, array $b) {
    return $a['baz'] - $b['baz'];
}

Dzięki PoWEr oF MATH zwraca wartość <0, 0 lub> 0 w zależności od tego, czy $ajest mniejsza, równa czy większa niż $b.

Zauważ, że to nie zadziała dobrze dla floatwartości, ponieważ zostaną one zredukowane do inti stracą precyzję. Użyj jawne -1, 0a1 zwracają wartości zamiast.

Obiekty

Jeśli masz tablicę obiektów, działa to w ten sam sposób:

function cmp($a, $b) {
    return $a->baz - $b->baz;
}

Funkcje

W funkcji porównawczej możesz zrobić wszystko, czego potrzebujesz, w tym funkcje wywoływania:

function cmp(array $a, array $b) {
    return someFunction($a['baz']) - someFunction($b['baz']);
}

Smyczki

Skrót do pierwszej wersji porównania ciągów znaków:

function cmp(array $a, array $b) {
    return strcmp($a['foo'], $b['foo']);
}

strcmpma dokładnie co się spodziewać cmptutaj, to wraca -1, 0albo 1.

Operator statku kosmicznego

PHP 7 wprowadził operatora statku kosmicznego , który ujednolica i upraszcza równe / mniejsze / większe niż porównania między typami:

function cmp(array $a, array $b) {
    return $a['foo'] <=> $b['foo'];
}

Sortowanie według wielu pól

Jeśli chcesz sortować głównie według foo, ale jeśli foojest równy dla dwóch elementów, sortuj według baz:

function cmp(array $a, array $b) {
    if (($cmp = strcmp($a['foo'], $b['foo'])) !== 0) {
        return $cmp;
    } else {
        return $a['baz'] - $b['baz'];
    }
}

Dla tych, którzy są zaznajomieni, jest to odpowiednik zapytania SQL z ORDER BY foo, baz.
Zobacz także bardzo zgrabną wersję skróconą i jak dynamicznie utworzyć taką funkcję porównania dla dowolnej liczby kluczy .

Sortowanie w ręcznym, statycznym porządku

Jeśli chcesz posortować elementy w „kolejności ręcznej”, takiej jak „foo”, „bar”, „baz” :

function cmp(array $a, array $b) {
    static $order = array('foo', 'bar', 'baz');
    return array_search($a['foo'], $order) - array_search($b['foo'], $order);
}

W związku z powyższym, jeśli używasz PHP 5.3 lub nowszego (i naprawdę powinieneś), użyj anonimowych funkcji, aby skrócić kod i uniknąć sytuacji, w której inna globalna funkcja będzie się zmieniać:

usort($array, function (array $a, array $b) { return $a['baz'] - $b['baz']; });

Tak proste może być sortowanie złożonej tablicy wielowymiarowej. Ponownie, pomyśl tylko w kategoriach uczenia PHP, jak powiedzieć, który z dwóch elementów jest „większy” ; pozwól PHP dokonać właściwego sortowania.

Również w przypadku wszystkich powyższych elementów, aby przełączać się między kolejnością rosnącą a malejącą, wystarczy zamienić argumenty $ai $b. Na przykład:

return $a['baz'] - $b['baz']; // ascending
return $b['baz'] - $a['baz']; // descending

Sortowanie jednej tablicy na podstawie innej

A potem jest osobliwy array_multisort, który pozwala sortować jedną tablicę na podstawie innej:

$array1 = array( 4,   6,   1);
$array2 = array('a', 'b', 'c');

Oczekiwany wynik to:

$array2 = array('c', 'a', 'b');  // the sorted order of $array1

Użyj, array_multisortaby się tam dostać:

array_multisort($array1, $array2);

Począwszy od PHP 5.5.0, możesz użyć array_columndo wyodrębnienia kolumny z tablicy wielowymiarowej i posortowania tablicy w tej kolumnie:

array_multisort(array_column($array, 'foo'), SORT_DESC, $array);

Począwszy od wersji 7.0.0 PHP można także wyodrębniać właściwości z tablicy obiektów.


Jeśli masz więcej typowych przypadków, edytuj tę odpowiedź.

deceze
źródło
Funkcja porównywania numerycznego nie działa dla wartości zmiennoprzecinkowych; Jestem pewien, że wiesz, co mam na myśli :)
Ja͢ck
1
W przypadku zamówienia statycznego chciałbym zastosować array_flip()szybsze wyszukiwanie pozycji, np. $order[$a['foo']]Zamiast array_search($a['foo'], $order).
Ja͢ck
Może to być trochę wielka edycja: gist.github.com/Rizier123/24a6248758b53245a63e839d8e08a32b, ale jeśli uważasz, że to ulepszenie i załączyłem wszystko, co niezbędne, mogę go zastosować.
Rizier123
@ Rizier123 Z pewnością pochwalam wysiłek, jest to bardzo dobry napis; ale wolałbym, żebyś opublikował to jako osobną odpowiedź, nawet jeśli jest bardzo podobna. Twoje przepisywanie zawiera wiele szczegółów (przekazanie przez odniesienie, duży stół itp.), Ale ten szczegół odwraca uwagę od płynnego wprowadzenia do głównego tematu działania funkcji porównawczej, IMHO. Celowo odnoszę się do instrukcji kilka razy, ponieważ właśnie tam należy szukać takich szczegółów; nie ma potrzeby powtarzać tego tutaj i odwracać uwagę od podstawowej idei, którą próbuję przekazać.
deceze
@deceze Głównym wyzwaniem, ponieważ jest to zadawanie pytań i odpowiedzi, jest wyświetlenie informacji tak kompaktowych i czytelnych, jak to możliwe, i ułatwienie użytkownikom znalezienia ich funkcji sortowania. Poprawiłem kilka rzeczy: gist.github.com/Rizier123/24a6248758b53245a63e839d8e08a32b, ale wciąż muszę się nad tym zastanowić, jeśli użyteczne i cenne jest opublikowanie go jako osobnej odpowiedzi, ponieważ jest to bardzo podobna treść
Rizier123
139

Cóż, najbardziej podstawowe metody są już objęte deceze. Chciałbym spojrzeć na inne rodzaje

Sortowanie za pomocą SPL

SplHeap

class SimpleHeapSort extends SplHeap {
    public function compare($a, $b) {
        return strcmp($a, $b);
    }
}

// Let's populate our heap here (data of 2009)
$heap = new SimpleHeapSort();
$heap->insert("a");
$heap->insert("b");
$heap->insert("c");

echo implode(PHP_EOL, iterator_to_array($heap));

Wynik

c
b
a

SplMaxHeap

Klasa SplMaxHeap zapewnia główne funkcje sterty, utrzymując maksimum na szczycie.

$heap = new SplMaxHeap();
$heap->insert(1);
$heap->insert(2);
$heap->insert(3);

SplMinHeap

Klasa SplMinHeap zapewnia główne funkcje sterty, utrzymując minimum na szczycie.

$heap = new SplMinHeap ();
$heap->insert(3);
$heap->insert(1);
$heap->insert(2);

Inne rodzaje sortowania

Sortowanie bąbelkowe

Z artykułu w Wikipedii na temat Sortowania baniek:

Sortowanie bąbelkowe, czasami niepoprawnie nazywane sortowaniem tonącym, to prosty algorytm sortowania, który działa poprzez wielokrotne przechodzenie przez listę do sortowania, porównywanie każdej pary sąsiednich elementów i zamianę ich, jeśli są w niewłaściwej kolejności. Przejście przez listę jest powtarzane, dopóki nie są potrzebne żadne zamiany, co wskazuje, że lista jest posortowana. Algorytm bierze swoją nazwę od sposobu, w jaki mniejsze elementy „bąbelkują” na górze listy. Ponieważ używa tylko porównań do działania na elementach, jest to rodzaj porównania. Chociaż algorytm jest prosty, większość innych algorytmów sortowania jest bardziej wydajna w przypadku dużych list.

function bubbleSort(array $array) {
    $array_size = count($array);
    for($i = 0; $i < $array_size; $i ++) {
        for($j = 0; $j < $array_size; $j ++) {
            if ($array[$i] < $array[$j]) {
                $tem = $array[$i];
                $array[$i] = $array[$j];
                $array[$j] = $tem;
            }
        }
    }
    return $array;
}

Sortuj wybór

Z artykułu w Wikipedii na temat sortowania:

W informatyce sortowanie według wyboru jest algorytmem sortowania, a konkretnie sortowaniem porównawczym na miejscu. Ma złożoność czasową O (n2), co czyni go nieefektywnym na dużych listach i ogólnie działa gorzej niż podobny rodzaj wstawiania. Sortowanie wyboru jest znane z jego prostoty i ma przewagę wydajności nad bardziej skomplikowanymi algorytmami w niektórych sytuacjach, szczególnie tam, gdzie pamięć pomocnicza jest ograniczona.

function selectionSort(array $array) {
    $length = count($array);
    for($i = 0; $i < $length; $i ++) {
        $min = $i;
        for($j = $i + 1; $j < $length; $j ++) {
            if ($array[$j] < $array[$min]) {
                $min = $j;
            }
        }
        $tmp = $array[$min];
        $array[$min] = $array[$i];
        $array[$i] = $tmp;
    }
    return $array;
}

Sortowanie przez wstawianie

Z artykułu w Wikipedii na temat sortowania wstawek:

Sortowanie wstawiane to prosty algorytm sortujący, który buduje końcową posortowaną tablicę (lub listę) pojedynczo. Jest znacznie mniej wydajny na dużych listach niż bardziej zaawansowane algorytmy, takie jak szybkie sortowanie, rozsypywanie lub sortowanie według scalania. Jednak sortowanie wstawiane ma kilka zalet:

function insertionSort(array $array) {
    $count = count($array);
    for($i = 1; $i < $count; $i ++) {

        $j = $i - 1;
        // second element of the array
        $element = $array[$i];
        while ( $j >= 0 && $array[$j] > $element ) {
            $array[$j + 1] = $array[$j];
            $array[$j] = $element;
            $j = $j - 1;
        }
    }
    return $array;
}

Shellsort

Z artykułu w Wikipedii na temat Shellsort:

Shellsort, znany również jako Shell sort lub metoda Shell, to sortowanie porównawcze na miejscu. Uogólnia sortowanie wymienne, takie jak wstawianie lub sortowanie bąbelkowe, poprzez rozpoczęcie porównywania i wymiany elementów z elementami, które są daleko od siebie, przed zakończeniem z sąsiednimi elementami.

function shellSort(array $array) {
    $gaps = array(
            1,
            2,
            3,
            4,
            6
    );
    $gap = array_pop($gaps);
    $length = count($array);
    while ( $gap > 0 ) {
        for($i = $gap; $i < $length; $i ++) {
            $tmp = $array[$i];
            $j = $i;
            while ( $j >= $gap && $array[$j - $gap] > $tmp ) {
                $array[$j] = $array[$j - $gap];
                $j -= $gap;
            }
            $array[$j] = $tmp;
        }
        $gap = array_pop($gaps);
    }
    return $array;
}

Sortuj według rodzaju grzebienia

Z artykułu w Wikipedii na temat sortowania Comb:

Sortowanie grzebienia jest stosunkowo prostym algorytmem sortowania, pierwotnie opracowanym przez Włodzimierza Dobosiewicza w 1980 roku. Później odkrył go Stephen Lacey i Richard Box w 1991 roku. Sortowanie grzebienia poprawia sortowanie bąbelkowe.

function combSort(array $array) {
    $gap = count($array);
    $swap = true;
    while ( $gap > 1 || $swap ) {
        if ($gap > 1)
            $gap /= 1.25;
        $swap = false;
        $i = 0;
        while ( $i + $gap < count($array) ) {
            if ($array[$i] > $array[$i + $gap]) {
                // swapping the elements.
                list($array[$i], $array[$i + $gap]) = array(
                        $array[$i + $gap],
                        $array[$i]
                );
                $swap = true;
            }
            $i ++;
        }
    }
    return $array;
}

Scal sortowanie

Z artykułu w Wikipedii na temat Sortowania:

W informatyce sortowanie przez scalanie (również często pisane scalanie) jest algorytmem sortowania opartym na O (n log n). Większość implementacji zapewnia stabilne sortowanie, co oznacza, że ​​implementacja zachowuje kolejność wprowadzania równych elementów w posortowanym wyjściu

function mergeSort(array $array) {
    if (count($array) <= 1)
        return $array;

    $left = mergeSort(array_splice($array, floor(count($array) / 2)));
    $right = mergeSort($array);

    $result = array();

    while ( count($left) > 0 && count($right) > 0 ) {
        if ($left[0] <= $right[0]) {
            array_push($result, array_shift($left));
        } else {
            array_push($result, array_shift($right));
        }
    }
    while ( count($left) > 0 )
        array_push($result, array_shift($left));

    while ( count($right) > 0 )
        array_push($result, array_shift($right));

    return $result;
}

Szybkie sortowanie

Z artykułu w Wikipedii na temat Quicksort:

Quicksort, czyli sortowanie z podziałem na partycje, to algorytm sortowania opracowany przez Tony'ego Hoare'a, który średnio dokonuje porównań O (n log n) w celu posortowania n pozycji. W najgorszym przypadku dokonuje porównań O (n2), chociaż takie zachowanie jest rzadkie.

function quickSort(array $array) {
    if (count($array) == 0) {
        return $array;
    }
    $pivot = $array[0];
    $left = $right = array();
    for($i = 1; $i < count($array); $i ++) {
        if ($array[$i] < $pivot) {
            $left[] = $array[$i];
        } else {
            $right[] = $array[$i];
        }
    }
    return array_merge(quickSort($left), array(
            $pivot
    ), quickSort($right));
}

Sortowanie permutacyjne

Z artykułu w Wikipedii na temat sortowania permutacyjnego:

Sortowanie permutacyjne, które polega na generowaniu możliwych permutacji tablicy / listy wejściowej do momentu znalezienia posortowanej.

function permutationSort($items, $perms = array()) {
    if (empty($items)) {
        if (inOrder($perms)) {
            return $perms;
        }
    } else {
        for($i = count($items) - 1; $i >= 0; -- $i) {
            $newitems = $items;
            $newperms = $perms;
            list($foo) = array_splice($newitems, $i, 1);
            array_unshift($newperms, $foo);
            $res = permutationSort($newitems, $newperms);
            if ($res) {
                return $res;
            }
        }
    }
}

function inOrder($array) {
    for($i = 0; $i < count($array); $i ++) {
        if (isset($array[$i + 1])) {
            if ($array[$i] > $array[$i + 1]) {
                return False;
            }
        }
    }
    return True;
}

Sortowanie Radix

Z artykułu w Wikipedii na temat sortowania Radix:

W informatyce sortowanie radix jest nieporównawczym algorytmem sortowania liczb całkowitych, który sortuje dane za pomocą kluczy liczb całkowitych poprzez grupowanie kluczy według poszczególnych cyfr, które dzielą tę samą znaczącą pozycję i wartość.

// Radix Sort for 0 to 256
function radixSort($array) {
    $n = count($array);
    $partition = array();

    for($slot = 0; $slot < 256; ++ $slot) {
        $partition[] = array();
    }

    for($i = 0; $i < $n; ++ $i) {
        $partition[$array[$i]->age & 0xFF][] = &$array[$i];
    }

    $i = 0;

    for($slot = 0; $slot < 256; ++ $slot) {
        for($j = 0, $n = count($partition[$slot]); $j < $n; ++ $j) {
            $array[$i ++] = &$partition[$slot][$j];
        }
    }
    return $array;
}
Baba
źródło
4
@deceze omówiłeś wszystkie podstawy .. musiałem poszukać innego sposobu, aby być istotnym :)
Baba
5
Nie widzę nic złego w bardziej akademickich metodach sortowania :) są one mniej przydatne w większości aplikacji, ale od czasu do czasu można je poprosić / wymagane jest przydatne, aby mieć odniesienie, zwłaszcza że z czasem zapomniałem o większości z nich
Dave
W rzeczywistości dla szybkiego sortowania zaleca się wybranie osi przestawnej jako mediany trzech wartości: pierwszego, środkowego i ostatniego elementu . To jest mój przykład seletionu przestawnego. Pozwala to uniknąć najgorszego przypadku tablicy posortowanej odwrotnie (co powoduje O(n^2)porównania, jeśli użyjemy tylko pierwszego elementu jako elementu przestawnego)
Alma Do
Słyszałem, że spl działa szybciej niż normalne sortowanie tablic. Czy to prawda?
jewelhuq
Zgadzam się z Dave'em, w dzisiejszych czasach prawie fw uwzględniło to, dlaczego rzadko go pamiętam lub używam.
Mike Nguyen,
43

Rodzaj stabilny

Powiedzmy, że masz taką tablicę:

['Kale', 'Kaleidoscope', 'Aardvark', 'Apple', 'Leicester', 'Lovely']

A teraz chcesz posortować tylko według pierwszej litery:

usort($array, function($a, $b) {
    return strcmp($a[0], $b[0]);
});

Wynik jest następujący:

['Apple', 'Aardvark', 'Kale', 'Kaleidoscope', 'Lovely', 'Leicester']

Ten rodzaj nie był stabilny!

Uważny obserwator mógł zauważyć, że algorytm sortowania tablic (QuickSort) nie dał stabilnego wyniku i że pierwotna kolejność między słowami tej samej pierwszej litery nie została zachowana. Ten przypadek jest trywialny i powinniśmy porównać cały ciąg, ale załóżmy, że twój przypadek użycia jest bardziej skomplikowany, na przykład dwa kolejne rodzaje na różnych polach, które nie powinny wzajemnie się anulować.

Transformacja Schwartziana

Transformacja Schwartziana , zwana także idiomem dekorowanie-sortowanie-undekorowanie, daje efekt stabilnego sortowania z wewnętrznie niestabilnym algorytmem sortowania.

Najpierw dekorujesz każdy element tablicy inną tablicą składającą się z klucza głównego (wartość) i klucza dodatkowego (jego indeks lub pozycja):

array_walk($array, function(&$element, $index) {
    $element = array($element, $index); // decorate
});

To przekształca tablicę w to:

[
    ['Kale', 0], ['Kaleidoscope', 1], 
    ['Aardvark', 2], ['Apple', 3], 
    ['Leicester', 4], ['Lovely', 5]
]

Teraz dostosowujemy krok porównania; porównujemy ponownie pierwszą literę, ale jeśli są takie same, klucz pomocniczy służy do zachowania pierwotnej kolejności:

usort($array, function($a, $b) {
    // $a[0] and $b[0] contain the primary sort key
    // $a[1] and $b[1] contain the secondary sort key
    $tmp = strcmp($a[0][0], $b[0][0]);

    if ($tmp != 0) {
        return $tmp; // use primary key comparison results
    }

    return $a[1] - $b[1]; // use secondary key
});

Następnie cofamy dekorację:

array_walk($array, function(&$element) {
    $element = $element[0];
});

Wynik końcowy:

['Aardvark', 'Apple', 'Kale', 'Kaleidoscope', 'Leicester', 'Lovely']

Co z ponownym użyciem?

Trzeba było przepisać funkcję porównania, aby pracować z transformowanymi elementami tablicy; możesz nie chcieć edytować swoich delikatnych funkcji porównawczych, więc oto opakowanie funkcji porównawczej:

function stablecmp($fn)
{
    return function($a, $b) use ($fn) {
        if (($tmp = call_user_func($fn, $a[0], $b[0])) != 0) {
            return $tmp;
        } else {
            return $a[1] - $b[1];
        }
    };
}

Napiszmy krok sortowania za pomocą tej funkcji:

usort($array, stablecmp(function($a, $b) {
    return strcmp($a[0], $b[0]);
}));

Voila! Twój dziewiczy kod porównania powrócił.

Jacek
źródło
Twoje zdanie „wpływa na stabilne sortowanie z wewnętrznie niestabilnym algorytmem sortowania” było dla mnie momentem ah-ha. Na stronie wikipedii nie ma wzmianki o słowie stabilnym, co wydaje mi się być pięknem transformacji. Wstyd.
Tyler Collier
1
@TylerCollier Tak, musisz czytać między wierszami tego odniesienia z Wikipedii ... Zaoszczędziłem ci kłopotów z robieniem tego ;-)
Ja͢ck
15

Począwszy od PHP 5.3 z zamknięciami, możliwe jest również użycie zamknięcia, aby określić kolejność twojego rodzaju.

Na przykład założenie, że $ tablica jest tablicą obiektów zawierających właściwość miesiąca.

 $orderArray = array("Jan","Feb","Mar","Apr","May","June","July","Aug","Sept","Oct","Nov","Dec");

 usort($array, function($a, $b) use ($orderArray){
       return array_search($a->month, $orderArray) - array_search($b->month, $orderArray);
 }); 
Orangepill
źródło
Pamiętaj tylko, że spowoduje to usunięcie dowolnej poprzedniej kolejności względnej (na przykład pierwszy obiekt „lipcowy” ze wstępnie posortowanej listy może skończyć na końcu grupy obiektów lipcowych po posortowaniu). Patrz „Sortowanie stabilne” powyżej.
George Langley,
9

LINQ

W .NET często używa się LINQ do sortowania, co zapewnia znacznie lepszą składnię w porównaniu z funkcjami porównywania, szczególnie gdy obiekty muszą być sortowane według wielu pól. Istnieje kilka portów LINQ do PHP, w tym biblioteka YaLinqo *. Dzięki niemu tablice mogą być sortowane za pomocą jednej linii bez pisania skomplikowanych funkcji porównawczych.

$sortedByName         = from($objects)->orderBy('$v->name');
$sortedByCount        = from($objects)->orderBy('$v->count');
$sortedByCountAndName = from($objects)->orderBy('$v->count')->thenBy('$v->name');

Porównania można dodatkowo dostosować, przekazując oddzwonienie jako drugi argument, na przykład:

$sortedByFilenameNat  = from($objects)->orderBy('$v->filename', 'strnatcmp');

Tutaj '$v->count'jest skrótem function ($v) { return $v->count; }(można użyć obu). Te łańcuchy metod zwracają iteratory, iteratory można przekształcić w tablice, dodając ->toArray()na końcu w razie potrzeby.

Wewnętrznie orderByi metody pokrewne wywołać odpowiednie funkcje sortowania tablicy ( uasort, krsort, multisort, usortitd.).

LINQ zawiera o wiele więcej metod inspirowanych SQL: filtrowanie, grupowanie, łączenie, agregowanie itp. Najlepiej nadaje się do przypadków, w których złożone transformacje tablic i obiektów muszą być wykonywane bez polegania na bazach danych.

* opracowane przeze mnie, zobacz plik Readme, aby uzyskać więcej informacji i porównanie z innymi portami LINQ

Athari
źródło
3

Sortowanie wielowymiarowe według wartości klucza

Naturalny rodzaj tablicy wielowymiarowej według wartości klucza, a także zachowaj pierwotną kolejność (nie tasuj głównych kluczy):

function multisortByKeyValue( $k, $arr ) {
    $ids   = array();
    $index = 1;

    foreach ( $arr as $key => $row ) {
        $ids[ $key ] = intval( $row[ $k ] ) . '-' . $index . '-' . $key;
        $index ++;
    }

    natsort( $ids );

    $arr = array_merge( $ids, $arr );

    return $arr;
}

Przypadek testowy:

$arr = array(
    'id1' => array(
        'label'    => 'ID 1',
        'priority' => 30,
    ),
    'id2' => array(
        'label'    => 'ID 2',
        'priority' => 70,
    ),
    'id3' => array(
        'label'    => 'ID 3',
        'priority' => 20,
    ),
    'id4' => array(
        'label'    => 'ID 4',
        'priority' => 30,
    ),
);

$sorted = multisortByKeyValue( 'priority', $arr );

// $sorted equals to:
/*
array (
  'id3' => array (
    'label' => 'ID 3',
    'priority' => 20,
  ),
  'id1' => array (
    'label' => 'ID 1',
    'priority' => 30,
  ),
  'id4' => array (
    'label' => 'ID 4',
    'priority' => 30,
  ),
  'id2' => array (
    'label' => 'ID 2',
    'priority' => 70,
  ),
)
*/
Andrew Surdu
źródło
2

Bardzo wygodne jest sortowanie tablic za pomocą funkcji sortowania z Nspl :

Podstawowe sortowanie

// Sort array
$sorted = sorted([3, 1, 2]);

// Sort array in descending order
$sortedDesc = sorted([3, 1, 2], true);

Sortowanie według wyniku funkcji

// Sort array by the result of a given function (order words by length)
$sortedByLength = sorted(['bc', 'a', 'abc'], 'strlen');
$sortedByLengthDesc = sorted(['bc', 'a', 'abc'], true, 'strlen');

// Sort array by the result of user-defined function (order words by the 1st character)
$sortedByTheFirstCharacter = sorted(['bc', 'a', 'abc'], function($v) { return $v[0]; }); 

// Which is the same as
$sortedByTheFirstCharacter = sorted(['bc', 'a', 'abc'], itemGetter(0));
$sortedByTheFirstCharacterDesc = sorted(['bc', 'a', 'abc'], true, itemGetter(0));

// itemGetter(0) returns a function which takes an argument with access by index/key
// and returns the value at index 0

Sortowanie tablicy wielowymiarowej

// Sort multidimensional array (sort list of users by their names)
$users = [
    array('name' => 'Robert', 'age' => 20),
    array('name' => 'Alex', 'age' => 30),
    array('name' => 'Jack', 'age' => 25),
];
$sortedByName = sorted($users, itemGetter('name'));
$sortedByNameDesc = sorted($users, true, itemGetter('name'));

// itemGetter('name') returns a function which takes an argument with access by index/key
// and returns the value of the 'name' key

Sortowanie tablicy obiektów

// Lets assume we have class User(name, age) with properties name and age
// and public methods getName() and getAge()
$users = [
    new User('Robert', 20),
    new User('Alex', 30),
    new User('Jack', 25),
];

// Sort list of objects by property value (sort list of users by their name)
$sortedByName = sorted($users, propertyGetter('name'));
$sortedByNameDesc = sorted($users, true, propertyGetter('name'));

// propertyGetter('name') returns a function which takes an object
// and returns the value of its 'name' property

// Sort list of objects by method result (sort list of users by their age)
$sortedByAge = sorted($users, methodCaller('getAge'));
$sortedByAgeDesc = sorted($users, true, methodCaller('getAge'));

// methodCaller('getAge') returns a function which takes an object
// and returns the result of its getAge() method

Sortowanie za pomocą funkcji porównania

// Sort with a comparison function (order words lexicographically with strcmp)
$sortedLexicographically = sorted(['bc', 'a', 'abc'], false, null, 'strcmp');

// Sort with user-defined comparison function (order words by the 1st character)
$sortedByTheFirstCharacter = sorted(['bc', 'a', 'abc'], false, null, function($v1, $v2) {
    return chr($v1[0]) - chr($v2[0]);
});

Możesz zobaczyć wszystkie te przykłady tutaj .

Ihor Burlachenko
źródło
2

Jeśli chcesz zamówić według wartości klucza, możesz to zrobić w jednej linii, elegancko i wyraźnie. To będzie sortować według ceny rosnąco. Wykorzystuje tablicę_multisort i tablicę_kolumny.

   Array([0] => Array ( [name] => eggs [price] => 1 ) [1] => Array ( [name] => coffee [price] => 9.99 ) [2] => Array ( [name] => rice [price] => 4.04 ) )

   array_multisort (array_column($array, 'price'), SORT_ASC, $array);

produkować

     Array ( [0] => Array ( [name] => eggs [price] => 1 ) [1] => Array ( [name] => rice [price] => 4.04 ) [2] => Array ( [name] => coffee [price] => 9.99 ) )
GAV
źródło
1

Ta strona jest bardzo obszerna, ale chcę dodać trochę więcej o niesamowitej użyteczności operatora statku kosmicznego (trójdrożny operator porównania) - pięknego potomka PHP7 +.

Wykorzystanie operatora statku kosmicznego do wdrożenia wielu warunków sortowania

To czyni wielkie postępy w zmniejszaniu wzdęć i poprawianiu czytelności.

Pisząc niestandardową funkcję sortowania ( usort()/ uasort()/ uksort()) w celu przetworzenia wielu warunków, wystarczy napisać zbalansowane tablice po obu stronach operatora i zwrócić wynik. Nigdy więcej zagnieżdżonych bloków warunkowych lub wielokrotnych zwrotów.

Elementy z obu stron operatora będą przemieszczane od lewej do prawej, po jednym, i zwracają ocenę, gdy tylko pojawi się brak powiązania lub gdy wszystkie elementy zostaną porównane.

Przykładowe dane z moich demonstracji:

$multidimArray = [
    'a' => [
        'boolean' => true,
        'natString' => 'text10',
        'object' => (object)['prop' => 2],
        'float' => -.5,
        'mixed' => []
    ],
    'b' => [
        'boolean' => true,
        'natString' => 'text12',
        'object' => (object)['prop' => 4],
        'float' => 0,
        'mixed' => null
    ],
    'c' => [
        'boolean' => false,
        'natString' => 'text100',
        'object' => (object)['prop' => 9],
        'float' => -.5,
        'mixed' => false
    ],
    'd' => [
        'boolean' => true,
        'natString' => 'text1',
        'object' => (object)['prop' => 9],
        'float' => -5,
        'mixed' => "\0"
    ],
    'e' => [
        'boolean' => false,
        'natString' => 'text2',
        'object' => (object)['prop' => 2],
        'float' => .5,
        'mixed' => ''
    ]
];

Demonstracje (aby uniknąć rozdęcia strony Stackoverflow, proszę zobaczyć link do wersji demonstracyjnych ):

  • Logika sortowania:

    1. boolean DESC (false = 0, true = 1, więc prawda przed falsem)
    2. float ASC

      uasort($multidimArray, function($a, $b) {
          return [$b['boolean'], $a['float']] <=> [$a['boolean'], $b['float']];
      });
  • Logika sortowania:

    1. mieszane ASC
    2. obiekt ASC
    3. boolean ASC

      uasort($multidimArray, function($a, $b) {
          return [$a['mixed'], $a['object']->prop, $a['boolean']] <=> [$b['mixed'], $b['object']->prop, $b['boolean']];
      });
  • Logika sortowania:

    1. liczba właściwości obiektu ASC
    2. iterowalność mieszanego DESC
    3. długość natString ASC
    4. natString ASC

      uasort($multidimArray, function($a, $b) {
          return [count(get_object_vars($a['object'])), is_iterable($a['mixed']), strlen($a['natString']), $a['natString']]
                 <=>
                 [count(get_object_vars($b['object'])), is_iterable($b['mixed']), strlen($b['natString']), $b['natString']];
      });

Ta składnia umożliwia eleganckie sortowanie wartości, wyników funkcjonalnych, głęboko zagnieżdżonych danych i kierunku sortowania. Jest to zdecydowanie warte umieszczenia paska narzędzi php ... na wypadek, gdy przetwarzasz dane inne niż baza danych - ponieważ oczywiście SQL byłby znacznie bardziej sensowną techniką.

Według własnego uznania, z PHP7.4 możesz używać składni strzałek z tymi anonimowymi funkcjami. Ten sam skrypt ze składnią strzałki .

mickmackusa
źródło
0

Jeśli ktoś chce prostszego rozwiązania do manipulowania tablicami, wystarczy użyć pakietu Laravel Collection, który ma zaimplementowaną funkcję sortBy, która pozwala na proste sortowanie według kluczy.

$collection->sortBy('forename')->sortBy('surname');

tzn. aby posortować najpierw według a, a następnie b, a następnie c, poprawną klauzulą ​​byłoby

sortBy('c')->sortBy('b')->sortBy('a')

https://packagist.org/packages/tightenco/collect

Rizerzero
źródło
-1

Istnieje kilka sposobów sortowania tablicy. Wymienię kilka metod wykonywania tego zadania. Po pierwsze podam tablicę liczb całkowitych nazywaną „$ numbers”.

$number = array(8,9,3,4,0,1,2);

Jest to normalny sposób tworzenia tablicy. Załóżmy, że chcę posortować tę tablicę w kolejności rosnącej. W tym celu można użyć metody „sort ()”.

<?php

    $number = array(8,9,3,4,0,1,2);
    sort($number);

   foreach ($number as $value) {
       echo $value."  ";
   }
?>

Teraz rozważ wynik tego,

wprowadź opis zdjęcia tutaj

Widać, że drukowana tablica liczb jest posortowana. Jeśli chcesz, aby ta tablica liczb była sortowana w porządku malejącym, do tego zadania można użyć metody „rsort ()”.

<?php

     $number = array(8,9,3,4,0,1,2);
     rsort($number);

     foreach ($number as $value) {
        echo $value."  ";
     }
?>

rozważ wynik.

wprowadź opis zdjęcia tutaj

Teraz tablica jest sortowana w kolejności malejącej. Ok, rozważmy tablicę asocjacyjną. Dam tablicę asocjacyjną (tablica asocjacyjna oznacza, że ​​tablica, której każdy indeks ma unikalną wartość klucza).

$number = array('eight'=>8,'nine'=>9,'three'=>3,'fore'=>4,'zero'=>0,'one'=>1,'two'=>2);

Teraz chcę posortować tę tablicę w porządku rosnącym według ich wartości. Można do tego użyć metody asort ().

<?php

   $number = array('eight'=>8,'nine'=>9,'three'=>3,'fore'=>4,'zero'=>0,'one'=>1,'two'=>2);
   asort($number);

   foreach ($number as $value) {
      echo $value."  ";
    }
?>

W przypadku sortowania malejącego według ich wartości można zastosować metodę „arsort ()”. Załóżmy, że chcesz posortować tę tablicę według ich wartości klucza. W tym można użyć metody „ksort ()”.

<?php

     $number = array('eight'=>8,'nine'=>9,'three'=>3,'fore'=>4,'zero'=>0,'one'=>1,'two'=>2);
     ksort($number);

     foreach ($number as $value) {
         echo $value."  ";
     }
?>

Teraz rozważ wynik. wprowadź opis zdjęcia tutaj

Teraz tablica jest sortowana według wartości klucza. Jeśli chcesz posortować tablicę w kolejności malejącej według wartości klucza, można użyć metody „krsort ()”.

<?php

    $number = array('eight'=>8,'nine'=>9,'three'=>3,'fore'=>4,'zero'=>0,'one'=>1,'two'=>2);
    krsort($number);

    foreach ($number as $value) {
       echo $value."  ";
    }
?>

Teraz tablica asocjacyjna jest sortowana w kolejności malejącej według wartości klucza. Spójrz na wynik. wprowadź opis zdjęcia tutaj

Oto niektóre metody sortowania tablicy w porządku rosnącym lub malejącym w php. Mam nadzieję, że uda ci się znaleźć pomysł.

GT_hash
źródło
Czy Deceze nie obejmuje już tych spostrzeżeń następującymi słowami: „Różnica między nimi polega jedynie na tym, czy zachowane są powiązania klucz-wartość (funkcje„ a ”), czy sortuje od niskiej do wysokiej, czy odwrotnie („ r ”), czy to sortuje wartości lub klucze („k”) i porównuje wartości („nat” vs. normalne) ”. w zaakceptowanej odpowiedzi?
mickmackusa,
-2

Najprostszym jest użycie funkcji usort do sortowania tablicy bez zapętlania: Poniżej znajduje się przykład:

   $array_compare= array("0" =>4,"1"=>2,"2"=>500,"3"=>100);

To posortuje w kolejności odfiltrowywania:

usort($array_compare, function($a, $b) {
        return ($b['x1'] - $a['x1']) > 0 ? 1 :-1;
    });

To posortuje w kolejności wysyłania:

usort($array_compare, function($a, $b) {
        return ($b['x1'] - $a['x1']) < 0 ? 1 :-1;
    });
pihu
źródło
1
1) Przykład i kod są niespójne. 2) Wyjaśniono to już szczegółowo w powyższych odpowiedziach. 3) Czy próbujesz odpowiedzieć na inne pytanie?
deceze