如何根据关联数组中的值对关联数组的索引数组进行重新排序?
How to re-sort an indexed array of associative arrays, based on the values in the associative arrays?
在我的 shell 代码中,我有一个索引数组,其中包含关联数组的名称:
declare -A assoc1=([name]=aaa [age]=20)
declare -A assoc2=([name]=bbb [age]=40)
declare -A assoc3=([name]=ccc [age]=25)
indexed_array=(assoc1 assoc2 assoc3)
因此,使用上面的方法,${indexed_array[@]}
等于 assoc1 assoc2 assoc3
。
我想要一个 sort_array
函数,它可以对 indexed_array
中的值进行重新排序,以便将年龄最大的关联数组 (assoc2) 列在最前面或最后,如下所示:
new_indexed_array=( $(echo ${indexed_array[@]} | sort_by 'age' 'desc') )
之后我应该在新数组中获得重新排序的内容:
declare -p new_indexed_array
# gives "assoc2 assoc3 assoc1"
我有一些样板代码可以进入数组值,但无法进一步对数组进行排序..
function sort_by {
# for each hash in the given array
get_stdin # (custom func, sets $STDIN)
for hash in ${STDIN[@]}
do
# get the hash keys
hash_keys="$(eval "echo ${!$hash[@]}")"
# for each key
for hashkey in $hash_keys
do
# reset
return_the_array=false
# if $hashkey matches the key given
if [ "$hashkey" = "" ];then
# check the value of this one if highest/lowest
# (compared to previous ones)
# and then return if yes/mo (asc/desc)
fi
# if $return_the_array = true, then we found the right key and
# it's higher/lower
if [ "$return_the_array" = true ];then
# do stuff
fi
done
done
}
如果您有 Bash 4.3 或更新版本,您可以为此使用 namerefs,如下所示:
sort_by() {
local arr field sort_params elem
declare -n arr=
field=
# Build array with sort parameters
[[ == 'desc' ]] && sort_params+=('-r')
[[ $field == 'age' ]] && sort_params+=('-n')
# Schwartzian transform
for elem in "${arr[@]}"; do
declare -n ref=$elem
printf '%s\t%s\n' "${ref["$field"]}" "$elem"
done | sort "${sort_params[@]}" | cut -f2
}
declare -A assoc1=([name]=aaa [age]=20)
declare -A assoc2=([name]=bbb [age]=40)
declare -A assoc3=([name]=ccc [age]=25)
indexed_array=(assoc1 assoc2 assoc3)
readarray -t byage < <(sort_by indexed_array age desc)
declare -p byage
readarray -t byname < <(sort_by indexed_array name asc)
declare -p byname
调用语法有点不同:
sort_by ARRAYNAME FIELD SORTORDER
并且输出是每行一个元素,因此要将其读回数组,我们必须使用类似 readarray
的方法(请参阅最后的示例)。
首先,我们使用 nameref 将数组名分配给 arr
:
declare -n arr=
arr
现在的行为就好像它是实际数组一样。
然后,我们用sort
的参数建一个数组:如果第三个参数是desc
,我们就用-r
,如果字段是age
,我们使用 -n
。这可以做得更聪明一些,并检查该字段是否包含数值,并相应地设置 -n
。
然后我们遍历 arr
的元素,其中的元素是关联数组的名称。在循环中,我们将名称分配给 ref
:
declare -n ref=$elem
ref
现在表现得像实际的关联数组。
为了排序,我们使用 Schwartzian transform(修饰 - 排序 - 取消修饰)打印带有所选字段名称的行,然后是数组名称;例如,对于 age
,我们会得到
20 assoc1
40 assoc2
25 assoc3
使用适当的参数将其通过管道传输到 sort
,然后使用 cut -f2
我们再次删除排序字段。
示例的输出如下所示:
declare -a byage=([0]="assoc2" [1]="assoc3" [2]="assoc1")
declare -a byname=([0]="assoc1" [1]="assoc2" [2]="assoc3")
注意 declare -n
在函数中声明局部参数,因此它们不会污染全局命名空间。
仅供参考,我使用的是已接受答案的修改版本:
function sort_by {
local field sort_params elem
field=
# Build array with sort parameters
[[ == 'desc' ]] && sort_params+=('-r')
[[ $field == 'age' ]] && sort_params+=('-n')
# Schwartzian transform,
# get piped array contents from $(cat)
while read -r elem; do
declare -n ref=$elem
printf '%s\t%s\n' "${ref["$field"]}" "$elem"
done | sort "${sort_params[@]}" | cut -f2 | tr '\n' ' '
}
这个函数和接受的答案之间的区别在于,上面的这个函数期望接收索引数组的内容作为管道输入,而不是将数组名作为第一个参数..
因此,我的函数可以这样调用:
echo ${someIndexedArray[@]} | sort_by 'age' 'desc'
(这个函数在同一行输出所有内容,而不是换行)
这个(对我来说)的好处是它现在可以在我使用的 CMS 中工作,而且 sort_by
函数不需要知道数组的名称 - 这不会是通过 CMS。
在我的 shell 代码中,我有一个索引数组,其中包含关联数组的名称:
declare -A assoc1=([name]=aaa [age]=20)
declare -A assoc2=([name]=bbb [age]=40)
declare -A assoc3=([name]=ccc [age]=25)
indexed_array=(assoc1 assoc2 assoc3)
因此,使用上面的方法,${indexed_array[@]}
等于 assoc1 assoc2 assoc3
。
我想要一个 sort_array
函数,它可以对 indexed_array
中的值进行重新排序,以便将年龄最大的关联数组 (assoc2) 列在最前面或最后,如下所示:
new_indexed_array=( $(echo ${indexed_array[@]} | sort_by 'age' 'desc') )
之后我应该在新数组中获得重新排序的内容:
declare -p new_indexed_array
# gives "assoc2 assoc3 assoc1"
我有一些样板代码可以进入数组值,但无法进一步对数组进行排序..
function sort_by {
# for each hash in the given array
get_stdin # (custom func, sets $STDIN)
for hash in ${STDIN[@]}
do
# get the hash keys
hash_keys="$(eval "echo ${!$hash[@]}")"
# for each key
for hashkey in $hash_keys
do
# reset
return_the_array=false
# if $hashkey matches the key given
if [ "$hashkey" = "" ];then
# check the value of this one if highest/lowest
# (compared to previous ones)
# and then return if yes/mo (asc/desc)
fi
# if $return_the_array = true, then we found the right key and
# it's higher/lower
if [ "$return_the_array" = true ];then
# do stuff
fi
done
done
}
如果您有 Bash 4.3 或更新版本,您可以为此使用 namerefs,如下所示:
sort_by() {
local arr field sort_params elem
declare -n arr=
field=
# Build array with sort parameters
[[ == 'desc' ]] && sort_params+=('-r')
[[ $field == 'age' ]] && sort_params+=('-n')
# Schwartzian transform
for elem in "${arr[@]}"; do
declare -n ref=$elem
printf '%s\t%s\n' "${ref["$field"]}" "$elem"
done | sort "${sort_params[@]}" | cut -f2
}
declare -A assoc1=([name]=aaa [age]=20)
declare -A assoc2=([name]=bbb [age]=40)
declare -A assoc3=([name]=ccc [age]=25)
indexed_array=(assoc1 assoc2 assoc3)
readarray -t byage < <(sort_by indexed_array age desc)
declare -p byage
readarray -t byname < <(sort_by indexed_array name asc)
declare -p byname
调用语法有点不同:
sort_by ARRAYNAME FIELD SORTORDER
并且输出是每行一个元素,因此要将其读回数组,我们必须使用类似 readarray
的方法(请参阅最后的示例)。
首先,我们使用 nameref 将数组名分配给 arr
:
declare -n arr=
arr
现在的行为就好像它是实际数组一样。
然后,我们用sort
的参数建一个数组:如果第三个参数是desc
,我们就用-r
,如果字段是age
,我们使用 -n
。这可以做得更聪明一些,并检查该字段是否包含数值,并相应地设置 -n
。
然后我们遍历 arr
的元素,其中的元素是关联数组的名称。在循环中,我们将名称分配给 ref
:
declare -n ref=$elem
ref
现在表现得像实际的关联数组。
为了排序,我们使用 Schwartzian transform(修饰 - 排序 - 取消修饰)打印带有所选字段名称的行,然后是数组名称;例如,对于 age
,我们会得到
20 assoc1
40 assoc2
25 assoc3
使用适当的参数将其通过管道传输到 sort
,然后使用 cut -f2
我们再次删除排序字段。
示例的输出如下所示:
declare -a byage=([0]="assoc2" [1]="assoc3" [2]="assoc1")
declare -a byname=([0]="assoc1" [1]="assoc2" [2]="assoc3")
注意 declare -n
在函数中声明局部参数,因此它们不会污染全局命名空间。
仅供参考,我使用的是已接受答案的修改版本:
function sort_by {
local field sort_params elem
field=
# Build array with sort parameters
[[ == 'desc' ]] && sort_params+=('-r')
[[ $field == 'age' ]] && sort_params+=('-n')
# Schwartzian transform,
# get piped array contents from $(cat)
while read -r elem; do
declare -n ref=$elem
printf '%s\t%s\n' "${ref["$field"]}" "$elem"
done | sort "${sort_params[@]}" | cut -f2 | tr '\n' ' '
}
这个函数和接受的答案之间的区别在于,上面的这个函数期望接收索引数组的内容作为管道输入,而不是将数组名作为第一个参数..
因此,我的函数可以这样调用:
echo ${someIndexedArray[@]} | sort_by 'age' 'desc'
(这个函数在同一行输出所有内容,而不是换行)
这个(对我来说)的好处是它现在可以在我使用的 CMS 中工作,而且 sort_by
函数不需要知道数组的名称 - 这不会是通过 CMS。