Biorąc pod uwagę tablicę ciągów, chciałbym posortować tablicę według długości każdego elementu.
Na przykład...
array=(
"tiny string"
"the longest string in the list"
"middle string"
"medium string"
"also a medium string"
"short string"
)
Powinien sortować do ...
"the longest string in the list"
"also a medium string"
"medium string"
"middle string"
"short string"
"tiny string"
(Jako bonus, byłoby miło, gdyby lista posortowała ciągi o tej samej długości, alfabetycznie. W powyższym przykładzie medium string
została posortowana wcześniej, middle string
mimo że są one tej samej długości. Ale to nie jest „trudne” wymaganie, jeśli nadmiernie komplikuje rozwiązanie).
Jest OK, jeśli tablica jest sortowana na miejscu (tj. „Tablica” jest modyfikowana) lub jeśli tworzona jest nowa sortowana tablica.
bash
shell-script
sort
array
PJ Singh
źródło
źródło
Odpowiedzi:
Jeśli ciągi nie zawierają znaków nowej linii, poniższe powinny działać. Sortuje indeksy tablicy według długości, używając samych łańcuchów jako dodatkowego kryterium sortowania.
Zauważ, że przejście na prawdziwy język programowania może znacznie uprościć rozwiązanie, np. W Perlu możesz to zrobić
źródło
sorted(array, key=lambda s: (len(s), s))
array.sort { |a| a.size }
Odczytuje wartości posortowanej tablicy z podstawienia procesu.
Podstawienie procesu zawiera pętlę. Pętla wyprowadza każdy element tablicy poprzedzony długością elementu i znakiem tabulacji pomiędzy nimi.
Sygnał wyjściowy obwodu jest sortowana liczbowo od największej do najmniejszej (a w porządku alfabetycznym, jeśli rozmiary są takie same, użycie
-k 2r
zamiast-k 2
odwrócenia kolejności alfabetycznej) oraz wynik , który jest wysyłany docut
którego usuwa kolumnę długości ciągów.Posortuj skrypt testowy, a następnie uruchom testowy:
Zakłada się, że ciągi nie zawierają znaków nowej linii. W systemach GNU z najnowszymi
bash
wersjami możesz obsługiwać osadzone znaki nowej linii w danych, używając znaku nul jako separatora rekordów zamiast znaku nowego wiersza:Tutaj dane są drukowane z ciągiem
\0
w pętli zamiast nowego wiersza,sort
icut
odczytuje linie rozdzielane zerami przez ich-z
opcje GNU, a nareadarray
koniec odczytuje dane rozdzielane zerami-d ''
.źródło
-d '\0'
w rzeczywistości jest-d ''
jakbash
nie można przekazać znaki NUL na polecenia, nawet jego builtins. Ale to rozumie-d ''
jako oznaczające delimit na NUL . Zauważ, że potrzebujesz do tego bash 4.4+.'\0'
, jest$'\0'
. I tak, konwertuje (prawie dokładnie) do''
. Ale to jest sposób, aby porozumieć się z innymi czytelnikami rzeczywista intencja przy użyciu separatora NUL.Nie będę całkowicie powtórzyć to, co już mówiłem o sortowaniu w bash , tylko ty możesz posortować ciągu bash, ale być może nie należy. Poniżej znajduje się tylko implementacja typu bash typu wstawiania, która jest O (n 2 ), a zatem jest tolerowana tylko dla małych tablic. Sortuje elementy tablicy w miejscu według ich długości, w malejącej kolejności. Nie wykonuje wtórnego sortowania alfabetycznego.
Jako dowód na to, że jest to wyspecjalizowane rozwiązanie, rozważ czasy istniejących trzech odpowiedzi dla tablic o różnych rozmiarach:
Choroba i Kusalananda mają dobry pomysł: oblicz raz długości i użyj dedykowanych narzędzi do sortowania i przetwarzania tekstu.
źródło
Hackish? (złożony) i szybki sposób jednoliniowy do sortowania tablicy według długości
( bezpieczny dla znaków nowej linii i rzadkich tablic):
W jednej linii:
Po wykonaniu
źródło
To także obsługuje elementy tablicy z nowymi liniami; działa, przechodząc
sort
tylko przez długość i indeks każdego elementu. Powinien współpracować zbash
iksh
.Jeśli elementy o tej samej długości również muszą zostać posortowane leksykograficznie, pętla może zostać zmieniona w następujący sposób:
To również przejdzie do
sort
łańcuchów (z nowymi wierszami zamienionymi na spacje), ale nadal będą kopiowane ze źródła do tablicy docelowej przez ich indeksy. W obu przykładach$(...)
będą widoczne tylko wiersze zawierające liczby (i/
znak w pierwszym przykładzie), więc nie zostaną wyzwolone przez globbing znaków lub spacji w łańcuchach.źródło
$(...)
podstawienie polecenia widzi tylko indeksy (listę liczb oddzielonych znakami nowej linii), ze względucut -d' ' -f1
na sortowanie po. Można to łatwo wykazaćtee /dev/tty
na końcu$(...)
.cut
.${!in[@]}
lub${#in[i]}/$i
zmiennych, ponieważ zawierają one tylko cyfry, które nie podlegają globalnej ekspansji, iunset IFS
zresetująIFS
spację, tabulator, nowy wiersz. W rzeczywistości, cytowanie ich byłoby szkodliwe , ponieważ dawałoby fałszywe wrażenie, że takie cytowanie jest użyteczne i skuteczne, i że ustawienieIFS
i / lub filtrowanie wynikówsort
w drugim przykładzie może być bezpiecznie zniesione.in
zawiera"testing * here"
ishopt -s nullglob
jest ustawiony przed pętlą.W przypadku, gdy przejście do
zsh
jest opcją, hackish droga tam (dla tablic zawierających dowolną sekwencję bajtów):zsh
pozwala definiować porządki sortowania dla jego globalnej ekspansji za pomocą globalnych kwalifikatorów. Więc tu jesteśmy oszukując go zrobić to dla dowolnych macierzy przez globbing na/
, ale zastępując/
z elementami macierzy (e'{reply=("$array[@]")}'
), a następnien
umericallyo
rder (w odwrotnej z dużą literąO
) elementów na podstawie ich długości (Oe'{REPLY=$#REPLY}'
).Pamiętaj, że jest to oparte na długości w liczbie znaków. Dla liczby bajtów ustaw ustawienia regionalne na
C
(LC_ALL=C
).Kolejne
bash
podejście 4.4+ (przy założeniu niezbyt dużej tablicy):(to długość w bajtach ).
W starszych wersjach
bash
zawsze możesz:(który będzie również współpracować z
ksh93
,zsh
,yash
,mksh
).źródło