Dada una matriz de cadenas, me gustaría ordenar la matriz de acuerdo con la longitud de cada elemento.
Por ejemplo...
array=(
"tiny string"
"the longest string in the list"
"middle string"
"medium string"
"also a medium string"
"short string"
)
Debería ordenar a ...
"the longest string in the list"
"also a medium string"
"medium string"
"middle string"
"short string"
"tiny string"
(Como beneficio adicional, sería bueno si la lista ordenara las cadenas de la misma longitud, alfabéticamente. En el ejemplo anterior medium string
se ordenó antes middle string
aunque sean de la misma longitud. Pero eso no es un requisito "difícil", si complica demasiado solución).
Está bien si la matriz se ordena in situ (es decir, se modifica "matriz") o si se crea una nueva matriz ordenada.
bash
shell-script
sort
array
PJ Singh
fuente
fuente
Respuestas:
Si las cadenas no contienen nuevas líneas, lo siguiente debería funcionar. Ordena los índices de la matriz por la longitud, utilizando las propias cadenas como criterio secundario de clasificación.
Tenga en cuenta que pasar a un lenguaje de programación real puede simplificar enormemente la solución, por ejemplo, en Perl, puede simplemente
fuente
sorted(array, key=lambda s: (len(s), s))
array.sort { |a| a.size }
Esto lee los valores de la matriz ordenada de una sustitución de proceso.
La sustitución del proceso contiene un bucle. El bucle genera cada elemento de la matriz antepuesto por la longitud del elemento y un carácter de tabulación intermedio.
La salida del bucle se ordena numéricamente de mayor a menor (y alfabéticamente si las longitudes son las mismas; úselas
-k 2r
en lugar de-k 2
revertir el orden alfabético) y el resultado de eso se envía ycut
elimina la columna con las longitudes de cadena.Ordenar script de prueba seguido de una ejecución de prueba:
Esto supone que las cadenas no contienen nuevas líneas. En los sistemas GNU con un reciente
bash
, puede admitir nuevas líneas incrustadas en los datos utilizando el carácter nul como separador de registros en lugar de nueva línea:Aquí, los datos se imprimen con un seguimiento
\0
en el bucle en lugar de líneas nuevas,sort
ycut
lee líneas delimitadas por nul a través de sus-z
opciones GNU yreadarray
finalmente lee los datos delimitados por nul con-d ''
.fuente
-d '\0'
en cuenta que, de hecho-d ''
,bash
no puede pasar caracteres NUL a los comandos, ni siquiera a sus incorporados. Pero sí entiende-d ''
como delimitación de significado en NUL . Tenga en cuenta que necesita bash 4.4+ para eso.'\0'
, lo es$'\0'
. Y sí, se convierte (casi exactamente) a''
. Pero esa es una manera de comunicar a otros lectores la intención real de usar un delimitador NUL.No voy a repetir por completo lo que ya he dicho acerca de la clasificación en bash , simplemente se puede clasificar dentro de fiesta, pero tal vez no deberías. A continuación se muestra una implementación solo de bash de un tipo de inserción, que es O (n 2 ), por lo que solo es tolerable para matrices pequeñas. Ordena los elementos de la matriz en su lugar por su longitud, en orden decreciente. No hace un orden alfabético secundario.
Como evidencia de que esta es una solución especializada, considere los tiempos de las tres respuestas existentes en varias matrices de tamaños:
Choroba y Kusalananda tienen la idea correcta: calcular las longitudes una vez y usar utilidades dedicadas para la clasificación y el procesamiento de texto.
fuente
Un hackish? (complejo) y una forma rápida de una línea para ordenar la matriz por longitud
( seguro para líneas nuevas y matrices dispersas):
En una línea:
En ejecución
fuente
Esto también maneja elementos de matriz con nuevas líneas en ellos; funciona pasando
sort
solo por la longitud y el índice de cada elemento. Debería funcionar conbash
yksh
.Si los elementos de la misma longitud también tienen que clasificarse lexicográficamente, el bucle podría cambiarse así:
Esto también pasará a
sort
las cadenas (con las nuevas líneas cambiadas a espacios), pero sus índices seguirán copiando de la matriz de origen a la de destino. En ambos ejemplos,$(...)
verá solo líneas que contengan números (y el/
carácter en el primer ejemplo), por lo que no se disparará mediante caracteres globales o espacios en las cadenas.fuente
$(...)
sustitución de comandos solo ve los índices (una lista de números separados por nuevas líneas), debido acut -d' ' -f1
la clasificación posterior. Esto podría ser fácilmente demostrado por untee /dev/tty
al final de la$(...)
.cut
.${!in[@]}
o${#in[i]}/$i
variables porque solo contienen dígitos que no están sujetos a la expansión global yunset IFS
restablecerán elIFS
espacio, la pestaña y la nueva línea. De hecho, citarlos sería perjudicial , ya que dará la falsa impresión de que dicha cita es útil y efectiva, y que la configuraciónIFS
y / o el filtrado de la salidasort
en el segundo ejemplo podría eliminarse de manera segura.in
contiene"testing * here"
yshopt -s nullglob
se establece antes del bucle.En caso de que cambiar a
zsh
sea una opción, una forma hackea allí (para matrices que contienen cualquier secuencia de bytes):zsh
permite definir órdenes de clasificación para su expansión global mediante calificadores globales. Así que aquí, nos estamos engañando a hacerlo por las matrices arbitrarias por parte de comodines en/
, pero sustituyendo/
con los elementos de la matriz (e'{reply=("$array[@]")}'
) y luegon
umericallyo
rder (a la inversa, con mayúsculaO
) los elementos en función de su longitud (Oe'{REPLY=$#REPLY}'
).Tenga en cuenta que se basa en la longitud en número de caracteres. Para el número de bytes, establezca la configuración regional en
C
(LC_ALL=C
).Otro
bash
enfoque 4.4+ (suponiendo una matriz no demasiado grande):(eso es longitud en bytes ).
Con versiones anteriores de
bash
, siempre puedes hacer:(que también funcionaría con
ksh93
,zsh
,yash
,mksh
).fuente