요소 길이를 기준으로 Bash 정렬 배열이 있습니까?

요소 길이를 기준으로 Bash 정렬 배열이 있습니까?

문자열 배열이 주어지면 각 요소의 길이를 기준으로 배열을 정렬하고 싶습니다.

예를 들어...

    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. 그러나 이것은 "하드"가 아닙니다. 해결 방법이 있는지 요청하세요.)

배열이 제자리에 정렬되었거나(즉, "배열"이 수정됨) 새로운 정렬된 배열이 생성된 경우에는 괜찮습니다.

답변1

문자열에 개행 문자가 포함되어 있지 않으면 다음이 작동합니다. 문자열 자체를 보조 정렬 기준으로 사용하여 배열의 인덱스를 길이별로 정렬합니다.

#!/bin/bash
array=(
    "tiny string"
    "the longest string in the list"
    "middle string"
    "medium string"
    "also a medium string"
    "short string"
)
expected=(
    "the longest string in the list"
    "also a medium string"
    "medium string"
    "middle string"
    "short string"
    "tiny string"
)

indexes=( $(
    for i in "${!array[@]}" ; do
        printf '%s %s %s\n' $i "${#array[i]}" "${array[i]}"
    done | sort -nrk2,2 -rk3 | cut -f1 -d' '
))

for i in "${indexes[@]}" ; do
    sorted+=("${array[i]}")
done

diff <(echo "${expected[@]}") \
     <(echo "${sorted[@]}")

실제 프로그래밍 언어로 이동하면 솔루션이 크게 단순화될 수 있습니다. 예를 들어 Perl에서는 다음을 수행할 수 있습니다.

sort { length $b <=> length $a or $a cmp $b } @array

답변2

readarray -t array < <(
for str in "${array[@]}"; do
    printf '%d\t%s\n' "${#str}" "$str"
done | sort -k 1,1nr -k 2 | cut -f 2- )

프로세스 교체에서 정렬된 배열의 값을 읽어옵니다.

프로세스 대체에는 루핑이 포함됩니다. 요소의 길이와 중간 탭이 앞에 오는 배열의 각 요소를 반복합니다.

루프의 출력은 숫자에 따라 가장 큰 것부터 가장 작은 것까지(또는 길이가 같은 경우 알파벳순으로, -k 2r대신 -k 2알파벳 순서를 반대로 사용하여) 정렬됩니다.저것cut문자열 길이의 열을 삭제하기 위해 전송됩니다 .

테스트 스크립트의 순서를 지정한 다음 테스트 실행을 수행합니다.

array=(
    "tiny string"
    "the longest string in the list"
    "middle string"
    "medium string"
    "also a medium string"
    "short string"
)

readarray -t array < <(
for str in "${array[@]}"; do
    printf '%d\t%s\n' "${#str}" "$str"
done | sort -k 1,1nr -k 2 | cut -f 2- )

printf '%s\n' "${array[@]}"
$ bash script.sh
the longest string in the list
also a medium string
medium string
middle string
short string
tiny string

이는 문자열에 개행 문자가 포함되어 있지 않다고 가정합니다. 최신 GNU를 사용하는 시스템에서는 bash레코드 구분 기호로 줄 바꿈 문자 대신 nul 문자를 사용하여 데이터에 새 줄 삽입을 지원할 수 있습니다.

readarray -d '' -t array < <(
for str in "${array[@]}"; do
    printf '%d\t%s\0' "${#str}" "$str"
done | sort -z -k 1,1nr -k 2 | cut -z -f 2- )

여기에서 데이터는 \0루프에서 개행 대신 후행으로 인쇄되고, nul로 구분된 행은 sortGNU 옵션을 통해 cut읽혀지며 , 마지막으로 nul로 구분된 데이터는 를 사용하여 읽습니다.-zreadarray-d ''

답변3

정확히 반복하지는 않겠습니다나는 이미 bash에서의 정렬에 대해 말했습니다., 오직 너할 수 있는Bash에서 정렬하지만 그렇게 하면 안 될 수도 있습니다. 다음은 삽입 정렬의 bash 전용 구현입니다. 이 구현은 시간 복잡도가 O(n 2 )이므로 작은 배열에만 적합합니다. 배열 요소를 길이에 따라 내림차순으로 정렬합니다. 보조 알파벳순 정렬을 수행하지 않습니다.

array=(
    "tiny string"
    "the longest string in the list"
    "middle string"
    "medium string"
    "also a medium string"
    "short string"
    )

function sort_inplace {
  local i j tmp
  for ((i=0; i <= ${#array[@]} - 2; i++))
  do
    for ((j=i + 1; j <= ${#array[@]} - 1; j++))
    do
      local ivalue jvalue
        ivalue=${#array[i]}
        jvalue=${#array[j]}
        if [[ $ivalue < $jvalue ]]
        then
                tmp=${array[i]}
                array[i]=${array[j]}
                array[j]=$tmp
        fi
    done
  done
}

echo Initial:
declare -p array

sort_inplace

echo Sorted:
declare -p array

이것이 전문화된 솔루션이라는 증거로 다양한 크기의 배열에 대한 세 가지 기존 답변의 타이밍을 고려하십시오.

# 6 elements
Choroba: 0m0.004s
Kusalananda: 0m0.004s
Jeff: 0m0.018s         ## already 4 times slower!

# 1000 elements
Choroba: 0m0.004s
Kusalananda: 0m0.004s
Jeff: 0m0.021s        ## up to 5 times slower, now!

5000 elements
Choroba: 0m0.004s
Kusalananda: 0m0.004s
Jeff: 0m0.019s

# 10000 elements
Choroba: 0m0.004s
Kusalananda: 0m0.006s
Jeff: 0m0.020s

# 99000 elements
Choroba: 0m0.015s
Kusalananda: 0m0.012s
Jeff: 0m0.119s

초로바그리고선행은 이루기가 어렵다올바른 생각을 가지십시오. 길이를 한 번 계산하고 정렬 및 텍스트 처리를 위한 전용 유틸리티를 사용하십시오.

답변4

zsh전환할 수 있는 옵션이 있는 경우 해킹이 있습니다(바이트 시퀀스를 포함하는 배열의 경우).

array=('' blah $'x\ny\nz' $'x\0y' '1 2 3')
sorted_array=( /(e'{reply=("$array[@]")}'nOe'{REPLY=$#REPLY}') )

zshglob 한정자를 통해 glob 확장의 정렬 순서를 정의할 수 있습니다. 따라서 여기서는 globbing을 통해 임의의 배열로 이 작업을 수행하도록 속입니다. 그러나 배열의 요소( ) /를 바꾼 다음 길이( )를 기준으로 요소를 숫자로(대문자가 아닌) 정렬합니다 ./e'{reply=("$array[@]")}'noOOe'{REPLY=$#REPLY}'

문자 길이를 기준으로 한다는 점에 유의하세요. 바이트 수의 경우 로캘을 C( LC_ALL=C)로 설정합니다.

또 다른 bash4.4+ 접근 방식(배열이 너무 크지 않다고 가정):

readarray -td '' sorted_array < <(
  perl -l0 -e 'print for sort {length $b <=> length $a} @ARGV
              ' -- "${array[@]}")

(길이는바이트).

이전 버전에서는 bash언제든지 다음을 수행할 수 있습니다.

eval "sorted_array=($(
    perl -l0 -e 'for (sort {length $b <=> length $a} @ARGV) {
      '"s/'/'\\\\''/g"'; printf " '\'%s\''", $_}' -- "${array[@]}"
  ))"

ksh93( , zsh, yash, 와 함께 사용할 수도 있습니다 mksh.)

관련 정보