How to create an array of unique elements from a string/array in bash?

If you are using zsh:

$ array=(1 2 3 2 1)
$ echo ${(u)array[@]}
1 2 3

or (if KSH_ARRAYS option is not set) even

$ echo ${(u)array}
1 2 3

With GNU awk (this also retains original order)

printf '%s\n' "1 2 3 2 1" | awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}'
1 2 3 

To read into a bash array

read -ra arr<<<$(printf '%s\n' "1 2 3 2 1" |
 awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}')
printf "%s\n"  "${arr[@]}"
1
2
3

For an array with arbitrary values, it's quite tricky with bash as it doesn't have a builtin operator for that.

bash however happens not to support storing NUL characters in its variables, so you can make use of that to pass that to other commands:

The equivalent of zsh's:

new_array=("${(@u}array}")

on a recent GNU system, could be:

eval "new_array=($(
  printf "%s\0" "${array[@]}" |
    LC_ALL=C sort -zu |
    xargs -r0 bash -c 'printf "%q\n" "$@"' sh
  ))"

Alternatively, with recent versions of bash, and assuming none of the array elements are empty, you could use associative arrays:

unset hash
typeset -A hash
for i in "${array[@]}"; do
  hash[$i]=
done
new_array=("${!hash[@]}")

With bash 4.4 and newer and with GNU sort:

readarray -td '' new_array < <(
  printf '%s\0' "${array[@]}" | LC_ALL=C sort -zu)

The order of the elements would not be the same in those different solutions.

With tcsh:

set -f new_array = ($array:q)

Would retain the first element (a b a => a b) like zsh's (u) expansion flag.

set -l new_array = ($array:q)

Would retain the last (a b a => b a). Those however remove empty elements from the array.