Dada uma matriz de seqüências de caracteres, eu gostaria de classificar a matriz de acordo com o comprimento de cada elemento.
Por exemplo...
array=(
"tiny string"
"the longest string in the list"
"middle string"
"medium string"
"also a medium string"
"short string"
)
Deve classificar para ...
"the longest string in the list"
"also a medium string"
"medium string"
"middle string"
"short string"
"tiny string"
(Como um bônus, seria bom se a lista classificasse seqüências de caracteres do mesmo tamanho em ordem alfabética. No exemplo acima, medium string
foi ordenada antes middle string
mesmo que sejam do mesmo tamanho. Mas esse não é um requisito "difícil", se complicar demais o solução).
Tudo bem se a matriz for classificada no local (por exemplo, "matriz" for modificada) ou se uma nova matriz classificada for criada.
bash
shell-script
sort
array
PJ Singh
fonte
fonte
Respostas:
Se as seqüências de caracteres não contiverem novas linhas, o seguinte deve funcionar. Ele classifica os índices da matriz pelo comprimento, usando as próprias seqüências de caracteres como o critério de classificação secundária.
Observe que mudar para uma linguagem de programação real pode simplificar bastante a solução, por exemplo, no Perl, você pode simplesmente
fonte
sorted(array, key=lambda s: (len(s), s))
array.sort { |a| a.size }
Isso lê os valores da matriz classificada de uma substituição de processo.
A substituição do processo contém um loop. O loop gera cada elemento da matriz precedido pelo comprimento do elemento e um caractere de tabulação no meio.
A saída do loop é classificada numericamente do maior para o menor (e alfabeticamente se os comprimentos forem iguais; use
-k 2r
no lugar de-k 2
para reverter a ordem alfabética) e o resultado disso é enviado para ocut
qual exclui a coluna com os comprimentos de string.Classifique o script de teste seguido por uma execução de teste:
Isso pressupõe que as seqüências não contenham novas linhas. Em sistemas GNU recentes
bash
, você pode oferecer suporte a novas linhas incorporadas nos dados usando o caractere nul como separador de registros em vez de nova linha:Aqui, os dados são impressos com rastreamento
\0
no loop, em vez de novas linhas,sort
ecut
lê linhas delimitadas por nulas através de suas-z
opções GNU e,readarray
finalmente, lê os dados delimitados por nula-d ''
.fonte
-d '\0'
na verdade-d ''
,bash
não é possível transmitir caracteres NUL para comandos, mesmo seus componentes internos. Mas entende-d ''
como delimitar o significado em NUL . Observe que você precisa do bash 4.4+ para isso.'\0'
, é$'\0'
. E sim, ele converte (quase exatamente) em''
. Mas essa é uma maneira de comunicar a outros leitores a intenção real de usar um delimitador NUL.Não repetirei completamente o que já disse sobre a classificação no bash , apenas você pode classificar no bash, mas talvez não deva. Abaixo está uma implementação somente bash de uma classificação de inserção, que é O (n 2 ) e, portanto, só é tolerável para matrizes pequenas. Ele classifica os elementos da matriz no local pelo comprimento, em ordem decrescente. Não faz uma classificação alfabética secundária.
Como evidência de que esta é uma solução especializada, considere os tempos das três respostas existentes em várias matrizes de tamanho:
Choroba e Kusalananda têm a idéia certa: calcule os comprimentos uma vez e use utilitários dedicados para classificação e processamento de texto.
fonte
Um hackish? (complexa) e rápida de uma linha para classificar a matriz por comprimento
( seguro para novas linhas e matrizes esparsas):
Em uma linha:
Em execução
fonte
Isso também lida com elementos de matriz com novas linhas; funciona passando
sort
apenas o comprimento e o índice de cada elemento. Deve funcionar combash
eksh
.Se os elementos do mesmo comprimento também precisarem ser classificados lexicograficamente, o loop poderá ser alterado assim:
Isso também passará para
sort
as strings (com novas linhas alteradas para espaços), mas elas ainda serão copiadas da fonte para a matriz de destino por seus índices. Nos dois exemplos,$(...)
ele verá apenas linhas contendo números (e o/
caractere no primeiro exemplo), para que não seja disparado por caracteres ou espaços em movimento nas strings.fonte
$(...)
substituição de comando vê apenas os índices (uma lista de números separados por novas linhas), por causa docut -d' ' -f1
depois da classificação. Isso pode ser facilmente demonstrado por umtee /dev/tty
no final do$(...)
.cut
.${!in[@]}
ou${#in[i]}/$i
variáveis, pois elas contêm apenas dígitos que não estão sujeitos à expansão glob eunset IFS
redefinirão oIFS
espaço, a guia e a nova linha. De fato, citá-las seria prejudicial , porque causaria a falsa impressão de que essa citação é útil e eficaz, e que a configuraçãoIFS
e / ou filtragem da saída dosort
segundo exemplo poderia ser eliminada com segurança.in
contém"testing * here"
eshopt -s nullglob
está definido antes do loop.No caso de alternar para
zsh
é uma opção, é uma maneira hackiana (para matrizes que contêm qualquer sequência de bytes):zsh
permite definir ordens de classificação para sua expansão glob através de qualificadores glob. Então, aqui, tentamos fazer isso para matrizes arbitrárias observando/
, mas substituindo/
pelos elementos da matriz (e'{reply=("$array[@]")}'
) e, em seguida,n
umericamente rdero
(ao contrário com maiúsculasO
) os elementos com base em seu comprimento (Oe'{REPLY=$#REPLY}'
).Observe que é baseado no tamanho do número de caracteres. Para o número de bytes, defina o código do idioma para
C
(LC_ALL=C
).Outra
bash
abordagem 4.4+ (assumindo uma matriz não muito grande):(isso é comprimento em bytes ).
Nas versões mais antigas do
bash
, você sempre pode:(que também iria trabalhar com
ksh93
,zsh
,yash
,mksh
).fonte