Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find duplicate elements in associative array with non consecutive index

i'm starting with bash and code in general and have a lot of pain to write this, can anyone tell me if it will work in every case please, maybe someone have a better approach? Many thanks in advance.

array=( [0]=24 [1]=24 [5]=10 [6]=24 [10]=24 [12]=12 )
KEYS=(${!array[@]})

for i in "${!KEYS[@]}"; do

    for j in "${!KEYS[@]}"; do

    if [[ $i -eq $j ]]; then
        continue
    fi

    if [[ ${array[${KEYS[$i]}]} -eq ${array[${KEYS[$j]}]} ]]; then

        duplicate+=( ${KEYS[$j]} )
    fi
    done

done

uniq=($(printf "%s\n" "${duplicate[@]}" | sort -u)); 

echo "${uniq[@]}"

EDIT:

my expected output is an array containing index of duplicated elements.

like image 335
user3353499 Avatar asked Feb 14 '23 11:02

user3353499


2 Answers

This approach has linear complexity (assuming constant time array lookups) instead of the quadratic complexity of the cascaded loops:

array=( [0]=24 [1]=24 [5]=10 [6]=24 [10]=24 [12]=12 )
ref=( )

for i in "${!array[@]}"; do
    ref[array[i]]="${ref[array[i]]}$i "
done

for i in "${!ref[@]}"; do
    [[ "${ref[i]% }" == *" "* ]] && echo "$i @ ${ref[i]% }"
done

The first loop copies the data from array[] to ref[], switching the roles of key and value and concatenating the new values in case of a collision (with blanks between the individual entries). So after the first loop ref[] will have the following content:

ref=( [10]="5 " [12]="12 " [24]="0 1 6 10 " )

The second loop prints the entries from ref[], but skips all entries that do not contain a blank, not counting trailing blanks, thus only printing those that point to two or more entries in array[].

Edit: Using slightly simpler version as suggested by Adrian in the comments.

like image 148
CliffordVienna Avatar answered May 16 '23 07:05

CliffordVienna


What is your $KEYS array for? You store the indices of $array inside it, but then you only use to reference those indices - this is unnecessary. Here is a script which does the same as your original post but without $KEYS:

array=( [0]=24 [1]=24 [5]=10 [6]=24 [10]=24 [12]=12 )

for i in "${!array[@]}"; do
    for j in "${!array[@]}"; do
        [ "$i" -eq "$j" ] && continue
        [ "${array[$i]}" -eq "${array[$j]}" ] && duplicate+=("$j")
    done
done

echo $(printf "%s\n" "${duplicate[@]}" | sort -u)

This prints out the indices of any duplicate values in your original array, all on one line - if you want them on separate lines, just put double quotes around the echo statement:

echo "$(printf "%s\n" "${duplicate[@]}" | sort -u)"
like image 42
Josh Jolly Avatar answered May 16 '23 08:05

Josh Jolly