比较两个排序的数组

时间:2013-08-01 08:32:52

标签: arrays bash

假设我有以下2个阵列。数组总是按字母顺序排序。

arr1=(a b c)
arr2=(a b c d)

我必须比较这两个数组,如果它们完全相同,则必须返回true。

4 个答案:

答案 0 :(得分:9)

我找到了一个可能的解决方案,使用STDIN和diff:

#!/bin/bash

arr1=(a b c)
arr2=(a b c d)

diff=$(diff <(printf "%s\n" "${arr1[@]}") <(printf "%s\n" "${arr2[@]}"))

if [[ -z "$diff" ]]; then
    echo "TRUE"
else
    echo "FALSE"
fi

编辑: 一点解释:

它会从数组中生成一个字符串,并通过STDIN将它们提供给diff。 diff或者返回差异或者没有任何内容,我将其填入变量并稍后使用-z测试内容。

答案 1 :(得分:7)

由于"$*"将所有位置参数作为单个单词获取,因此可以实现:

[ "${arr1[*]}" == "${arr2[*]}" ] && echo "equal" || echo "distinct"

请注意,表达式[ condition ] && echo "equal" || echo "distinct"等同于

if [ condition ]; then
   echo "equal"
else
   echo "distinct"
fi

测试

$ arr1=(a b c)
$ arr2=(a b c d)
$ [ "${arr1[*]}" == "${arr2[*]}" ] && echo "equal" || echo "distinct"
distinct
$ arr2=(a b c)
$ [ "${arr1[*]}" == "${arr2[*]}" ] && echo "equal" || echo "distinct"
equal

当元素没有空格时,它只能100%工作(参见注释中的讨论)。

答案 2 :(得分:3)

我能想到的最强大的方式是:

  • 首先比较数组大小
  • 逐元素比较

所以:

arr1=(1 2 3 )
arr2=(1 2 "3 4")

[ ${#arr1[*]} != ${#arr2[*]} ] && { echo arrays different size; exit 1; }
for ii in ${!arr1[*]}; do
     [ "${arr1[$ii]}" == "${arr2[$ii]}" ] || { echo different element $ii; exit 1; }
done
echo arrays identical
exit 0

使用的构造是:

  • ${#array[*]}返回数组中元素的数量
  • ${!array[*]}返回索引列表(而不是返回元素的${array[*]}

上面应该处理数组值,稀疏数组和关联数组中的空格(虽然它不总是捕获关联数组中的不同索引,但是你需要额外的测试)。

答案 3 :(得分:2)

对于那些想要使用函数而不是直接检查的解决方案的人来说,这是我写的解决方案。后者适用于普通数组,关联数组和稀疏数组。但它至少需要Bash 4.3。

这个函数是我写的lib的一部分。作为我的lib中的约定,我使用返回值和retval全局参数来表示比简单数字更复杂的return语句。这同样适用于errorWithLog,它只是stderr上的回显,如果后者可用,则记录为系统记录器的错误。

#-------------------------------------------------------------------------------
# I: - The array to compare tableA[@]
#    - The second array tableB[@] against which to compare the first array
# P: Search if both arrays (all types) are equal
#    NOTE: The tables must be passed *AS NAME* as myTable not as $myTable[@]
#    nor ${myTable[@]} and requires Bash 4.3
#    Inspired from http://stackoverflow.com/a/17990637/3514658
#    and from http://stackoverflow.com/a/4017175/3514658
# O: - If both arrays are equal:
#       - retval: true
#       - return value for direct usage in a if statement: 0
#    - If both arrays are not equal:
#       - retval: false
#       - return value for direct usage in a if statement: 1
#-------------------------------------------------------------------------------
function isArraysEqual() {

    # Accessing by dereference using -n is new in Bash 4.3.
    local -n arr1=$1 2>/dev/null
    local -n arr2=$2 2>/dev/null
    # If <Bash 4.3, need to use the following syntax, but checking the keys of
    # associative arrays is not supported and, in that case, tables must be
    # passed as names *WITH* elements i.e.: myTable[@]
    # local -a arr1=("${!1}")
    # local -a arr2=("${!2}")
    if [ $? -ne 0 ]; then
        errorWithLog "isArraysEqual() accessing using dereference with -n"\
            "needs at least Bash 4.3. Arrays reported as different."
        retval=false
        return 1
    fi

    # Check size first. This is way faster than checking each item over
    # iteration.
    if [ ${#arr1[@]} != ${#arr2[@]} ]; then
        retval=false
        return 1
    fi

    # The ! expands to a list of array keys. For normal arrays, not associative
    # arrays, this gives a list of index values starting from 0.
    local -a arr1Keys=("${!arr1[@]}")
    local -a arr2Keys=("${!arr2[@]}")
    for (( i = 0; i < ${#arr1[@]}; i += 1 )); do

        key=${arr1Keys[$i]}

        # Check if the values are the same. If the key does not exist in arr2
        # and the key does exist but is null in arr1, the values are NOT
        # considered different. This is why checking keys is mandatory.
        if [ "${arr1[$key]}" != "${arr2[$key]}" ]; then
            retval=false
            return 1
        fi

        # Check if keys are the same. This is needed for associative arrays.
        if [ "${arr1Keys[$i]}" != "${arr2Keys[$i]}" ]; then
            retval=false
            return 1
        fi
    done
    retval=true
    return 0
}

用法示例:

declare -A table1=([hello]=world [ab]=cd)
declare -A table2=([hello]=world [ab]=cd)

if isArraysEqual table1[@] table2[@]; then
    echo "yes"
else
    echo "no"
fi

提供yes

declare -A table1=([hello]=world [ab]=cd)
declare -A table2=([hello]=world [ab]=cde)

if isArraysEqual table1 table2; then
    echo "yes"
else
    echo "no"
fi

提供no

declare -A table1=([hello]=world [abhe]=ce)
declare -A table2=([hello]=world [ab he]=ce)

if isArraysEqual table1 table2; then
    echo "yes"
else
    echo "no"
fi

提供no

table1=(1 2 3 4)
table2=(1 2 "3 4")

if isArraysEqual table1 table2; then
    echo "yes"
else
    echo "no"
fi

提供no