Sorting an array based on substring of each element

arrayzsh

How does one sort an array based on a substring of each element ?
e.g. given an array like

arr=( 2some05stuff 4more02stuff 3evenmore01stuff 1no04stuff )

I'd like to sort the elements by the numerical string that precedes stuff so I end up with

3evenmore01stuff
4more02stuff
1no04stuff
2some05stuff

I know that parameter expansion flags o/O reorder an array e.g.

print -rl "${(@on)arr}"
1no04stuff
2some05stuff
3evenmore01stuff
4more02stuff

and

print -rl "${(@On)arr}" 
4more02stuff
3evenmore01stuff
2some05stuff
1no04stuff

I just don't know if it's possible to combine them flags with a function or other parameter expansion like e.g. subscript removal or subscript expansion…

Best Answer

I don't know of a way to do this inline. With globs qualifiers, you can specify a sort key (*(oe\''REPLY=${${REPLY%stuff*}##*[^0-9]}'\')), but there's no such thing with parameter expansion modifiers.

What you can do is build an auxiliary array containing ${sort_key}$'\0'${value}, i.e. append a null byte and the original value to the sort key. Assuming that the sort keys don't contain null bytes, sorting that array gives the desired order, and stripping off the sort key prefix gives the desired result.

typeset -a tmp; tmp=(); typeset -i i
for ((i=1; i<=$#arr; i++)); do tmp[$i]=${${arr[$i]%stuff*}##*[^0-9]}$'\0'$arr[$i]; done
print -lr "${(@)${(@o)tmp}#*$'\0'}"
Related Question