测试数组中是否存在重复值

测试数组中是否存在重复值

我正在尝试找到一种简单的方法来测试数组中的重复值。能够识别具有重复项的特定行固然很好,但并非完全必要,但重要的一点是能够看到存在重复项。

我有一个数组,$key_array其中包含一些数字:

# echo ${key_array[@]}
1 2 3 4 3 3

该数组可以有任意数量的数字,其中一些数字可能与其他数字重复。它们只是整数。 (以 a 开头的数字0,例如03,根本不应该进入数组,但万一发生这种情况,捕获303作为彼此的重复项比将它们视为不同的数字更好。)

我需要确定这些数字是否重复。我想如果没有别的办法的话,这可以通过退出代码来完成。我所追求的是这样的:

if $(some command); then
 echo "Array contains duplicates."
 exit 1
fi
$(commands to run after duplicate check)

最后的想法是,如果存在重复项,脚本会通知用户并退出(对于识别重复项在哪里并不是非常重要,只需告诉用户检查重复项就足够了),或者如果没有任何重复项,它继续运行并运行许多其他东西。

我怎样才能最好地完成这个任务?

答案1

zsh外壳中:

array=(1 2 3 4 3 3)
if (($#array != ${#${(u)array}})); then
  print -u2 array contains duplicates
  exit 1
fi

where${(u)array}扩展为数组的唯一元素,因此我们只是将元素数与唯一元素数进行比较。

shellbash没有等效的东西,但由于它的数组无论如何都不能包含 NUL 字节,如果您使用的是 GNU 系统,您可以执行以下操作:

readarray -td '' dups < <(
  (( ${#array[@]} == 0 )) ||
    printf '%s\0' "${array[@]}" |
      LC_ALL=C sort -z |
      LC_ALL=C uniq -zd
)

if ((${#dups[@]} > 0)); then
  echo >&2 "array has duplicates:"
  printf >&2 ' - "%s"\n' "${dups[@]}"
  exit 1
fi

其中,元素被考虑复制如果它们是逐字节相同的,而不是它们的数值(如果有的话)相同(1, 01, 0x1, 1e0, 2-1, $'1\n',' 1'都被认为是不同的)。

答案2

假设arr仅包含整数并且零填充数字应被视为重复(例如,01是 的重复1),我们可以使用第二个数组来保留在解析第一个数组 的每个元素时已经“看到”的值arr

#!/bin/bash
arr=(1 2 3 4 3 3)
seen=()

for i in "${arr[@]}"; do
    #Remove padding zeroes, if any
    i=$((10#$i))
    # If element of arr is not in seen, add it as a key to seen
    if [ -z "${seen[i]}" ]; then
        seen[i]=1
    else
        echo "Array contains a duplicate."
        break
    fi
done

答案3

如果您需要它在 Bash 3.X 中工作,您可以使用uniq

IFS=$'\n' sort <<<"${key_array[*]}" | uniq -d; unset IFS

这将返回且仅返回数组的所有重复元素。

描述

  1. IFS=$'\n'设置内部字段分隔符到一个新的行字符,确保"${key_array[*]}"每个数组元素扩展为一行。
  2. <<<是一个这里的字符串将 的输出输入"${key_array[*]}"到 的标准输入中sort
  3. sort好吧,排序。
  4. uniq -d输出“...输入中重复的每一行的单个副本。” (从man uniq)。
  5. unset IFS生意很好,并重置IFS回默认值。

答案4

假设你的key_array数组仅包含整数(正整数),我们可以使用普通数组的事实bash壳里。以下代码在实例化常规数组中的元素时循环遍历键数组,直到找到已经处理过的键:

key_array=( '09' 1 2 3 4 3 3 '04' '001' '07' )

has_dupes () (
        unset -v a

        for key do
                ${a[10#$key]+'return'}  # execute "return" if a[10#$key] is set
                a[10#$key]=             # set a[10#$key] to empty string
        done

        return 1
)

if has_dupes "${key_array[@]}"; then
        echo 'array has dupes'
else
        echo 'array has no dupes'
fi

这引入了一个实用函数 ,has_dupes它接受一个整数列表,如果列表中有重复值则返回零,如果没有重复值则返回非零。

标准参数扩展${variable+word}用于插入先前设置的单词returnif 。a[10#$key]return被替换时,它终止函数的执行并向调用者返回零退出状态,表示我们发现了重复值。索引的意思是“解释为以 10 为基数的10#$key整数的值”,并允许我们将键等同起来,例如和。$key033

相关内容