Учитывая массив строк, я хотел бы отсортировать массив в соответствии с длиной каждого элемента.
Например...
array=(
"tiny string"
"the longest string in the list"
"middle string"
"medium string"
"also a medium string"
"short string"
)
Должен сортировать в ...
"the longest string in the list"
"also a medium string"
"medium string"
"middle string"
"short string"
"tiny string"
(В качестве бонуса было бы неплохо, если бы список сортировал строки одинаковой длины в алфавитном порядке. В приведенном выше примере medium string
сортировка выполнялась раньше, middle string
даже если они имеют одинаковую длину. Но это не является "жестким" требованием, если оно усложняет решение).
Это нормально, если массив отсортирован на месте (т. Е. «Массив» изменен) или если создан новый отсортированный массив.
bash
shell-script
sort
array
Пи Джей Сингх
источник
источник
Ответы:
Если строки не содержат символов новой строки, должно работать следующее. Он сортирует индексы массива по длине, используя сами строки в качестве вторичного критерия сортировки.
Обратите внимание, что переход на настоящий язык программирования может значительно упростить решение, например, в Perl вы можете просто
источник
sorted(array, key=lambda s: (len(s), s))
array.sort { |a| a.size }
Это читает значения отсортированного массива из подстановки процесса.
Подстановка процесса содержит цикл. Цикл выводит каждый элемент массива с добавлением длины элемента и символа табуляции между ними.
Выход из цикла сортируется численно от большего к меньшему (и по алфавиту , если длины один и то же, использование
-k 2r
на месте ,-k 2
чтобы полностью изменить алфавитный порядок) и результат , который посылается ,cut
который удаляет столбец с длиной строки.Сортировать тестовый скрипт с последующим тестовым запуском:
Это предполагает, что строки не содержат символов новой строки. В системах GNU с последними
bash
версиями вы можете поддерживать встроенные символы новой строки в данных, используя символ nul в качестве разделителя записей вместо символа новой строки:Здесь данные печатаются с
\0
завершением в цикле, а не с новыми строками ,sort
иcut
считывает строки, разделенные нулями, через их-z
параметры GNU и,readarray
наконец, считывает данные, разделенные нулями, с помощью-d ''
.источник
-d '\0'
это на самом деле ,-d ''
какbash
не может передать NUL символы команд, даже его встроенные функции . Но он понимает-d ''
как значение разграничения на NUL . Обратите внимание, что для этого вам нужен bash 4.4+.'\0'
так$'\0'
. И да, он преобразует (почти точно) в''
. Но это способ донести до других читателей реальное намерение использовать разделитель NUL.Я не буду полностью повторять то, что я уже сказал о сортировке в bash , просто вы можете сортировать в bash, но, возможно, не следует. Ниже приведена реализация сортировки с вставкой только для bash, которая имеет значение O (n 2 ) и поэтому допустима только для небольших массивов. Он сортирует элементы массива по их длине в порядке убывания. Это не делает вторичный алфавитный вид.
В качестве доказательства того, что это специализированное решение, рассмотрим временные характеристики существующих трех ответов для массивов разных размеров:
Чороба и Кусалананда имеют правильную идею: вычислить длины один раз и использовать специальные утилиты для сортировки и обработки текста.
источник
Хакский? (сложный) и быстрый однострочный способ сортировки массива по длине
( безопасно для новых строк и разреженных массивов):
На одной строке:
На исполнение
источник
Это также обрабатывает элементы массива с символами новой строки в них; это работает, передавая
sort
только длину и индекс каждого элемента. Это должно работать сbash
иksh
.Если элементы одинаковой длины также должны быть отсортированы лексикографически, цикл можно изменить следующим образом:
Это также будет передавать
sort
строки (с символами новой строки, замененными на пробелы), но они все равно будут скопированы из исходного массива в целевой по своим индексам. В обоих примерах,$(...)
будут видны только строки, содержащие цифры (и/
символ в первом примере), поэтому он не будет отключен с помощью символов или пробелов в строках.источник
$(...)
команда подстановки видит только индексы (список чисел, разделенных новыми строками) из-cut -d' ' -f1
за сортировки после. Это может быть легко продемонстрированоtee /dev/tty
в конце$(...)
.cut
.${!in[@]}
или${#in[i]}/$i
расширения переменных, потому что они содержат только цифры, которые не подлежат расширению glob, иunset IFS
будут сброшены вIFS
пробел, табуляцию, перевод строки. На самом деле, цитирование их было бы вредно , потому что это создаст ложное впечатление, что такое цитирование полезно и эффективно, и что настройкаIFS
и / или фильтрация выводаsort
во втором примере может быть безопасно устранена.in
содержится"testing * here"
иshopt -s nullglob
устанавливается перед циклом.В случае, если переключение на
zsh
является опцией, есть хакерский способ (для массивов, содержащих любую последовательность байтов):zsh
позволяет определять порядки сортировки для его расширения глоба через квалификаторы глоба. Так вот, мы обманывая его , чтобы сделать это для любых массивов, подстановки на/
, но заменяя/
с элементами массива (e'{reply=("$array[@]")}'
) , а затемn
umericallyo
rder (в обратном с прописными буквамиO
) элементы на основе их длины (Oe'{REPLY=$#REPLY}'
).Обратите внимание, что он основан на длине в количестве символов. Для количества байтов установите локаль в
C
(LC_ALL=C
).Другой
bash
подход 4.4+ (при условии, что массив не слишком большой):(это длина в байтах ).
С более старыми версиями
bash
вы всегда можете сделать:(который также будет работать с
ksh93
,zsh
,yash
,mksh
).источник