#! /bin/bash
list2ranges () {
local -a arr
IFS=, arr=($1)
local min=${arr[0]}
local max=${arr[0]}
local -A seen
local e
for e in "${arr[@]}" ; do
(( e < min )) && min=e
(( e > max )) && max=e
seen[$e]=1
done
local -a out
local i
for ((i=min; i<=max; ++i)) ; do
if [[ ${seen[$i]} ]] ; then
if [[ ${seen[$((i-1))]} ]] ; then
if [[ ${out[${#out[@]}-1]} != *- ]] ; then
out[${#out[@]}-1]+=-
fi
if [[ ! ${seen[$((i+1))]} ]] ; then
out[${#out[@]}-1]+=$i
fi
else
out+=($i)
fi
fi
done
IFS=, echo "${out[*]}"
}
list=8,9,10,25,47,48,49,50,51,52,53,54,55,102,107,111,201,202,203,204,205
diff <(list2ranges "$list") <(echo 8-10,25,47-55,102,107,111,201-205)
#!/bin/bash
list=8,9,10,25,47,48,49,50,51,52,53,54,55,102,107,111,201,202,203,204,205
first=0 ## first value written flag
inseq=0 ## in sequence flag
while read -r n; do ## read each of the sorted values
## if first not written, write, set last to current and set first flag
[ "$first" -eq 0 ] && { printf "%s" "$n"; last="$n"; first=1; continue; }
## if current minus last is 1 -- in sequence, set inseq flag
if ((n - last == 1)); then
inseq=1
elif [ "$inseq" -eq 1 ]; then ## if not sequential
printf -- "-%s,%s" "$last" "$n" ## finish with last, output next
inseq=0 ## unset inseq flag
else
printf ",%s" "$n" ## individual number, print
fi
last="$n" ## set last to current
## process substitution sorts list with printf trick
done < <(IFS=$','; printf "%s\n" $list | sort -n)
## output final term either ending sequence or as individual number
if [ "$inseq" -gt 0 ]; then
printf -- "-%s" "$last"
else
printf ",%s" "$last"
fi
echo "" ## tidy up with a newline
3条答案
按热度按时间dohp0rv51#
由于只有
shell
和bash
被标记,因此这里有一个基于Parameter Expansion的bash解决方案:这将遍历数字列表,并对每个新数字相应地调整目标字符串的最后一部分。
个字符
for n in ${arrayvar[@]}; do
一样。arrayvar=(${strvar//,/ })
将其转换为数组,或者直接在转换for n in ${strvar//,/ }; do
时进行转换。for
循环改为while read -r n; do
。sort -n
是最简单的方法。mapfile -t arrayvar < <(printf '%s\n' "${arrayvar[@]}" | sort -n)
,或者按照here的解决方案:IFS=$'\n' arrayvar=($(sort -n <<< "${arrayvar[*]}")); unset IFS
。trnvg8h32#
找到列表中的最小值和最大值。将所有数字作为键存储在一个关联数组中。然后,从最小值到最大值重新排列所有数字,如果数字-1不在关联数组中,则开始一个新的范围。如果它在那里,则范围继续,如果数字+1不在那里,则范围到此结束。
字符串
如果最小值和最大值之间的差异很大,这将花费很长时间。考虑使用Perl与Set::IntSpan代替。
型
mcdcgff03#
好吧,这是一个有趣的小问题,值得添加另一种方法。
你可以通过保持一对夫妇的旗帜使这项任务更容易(或状态)变量。
1
或0
的简单变量(true或false),它会跟踪您是否已经写入了第一个值,以及您是在一个数字序列中还是在连续数字的范围之间。当您循环值时,如果您保留上一次迭代中的最后一个值,任务变得相当容易。例如,您可以:
字符串
您声明您的数字没有按顺序排列,因此
while()
循环由一个进程替换提供,该进程替换在读取值之前使用sort -n
对值进行排序。(进程替换中未加引号的$list
是故意的)示例使用/输出
型