OscarAkaElvis
OscarAkaElvis

Reputation: 5714

Bash. Associative array iteration (ordered and without duplicates)

I have two problems handling associative arrays. First one is that I can't keep a custom order on it.

#!/bin/bash

#First part, I just want to print it ordered in the custom created order (non-alphabetical)
declare -gA array
array["PREFIX_THIS","value"]="true"
array["PREFIX_IS","value"]="false"
array["PREFIX_AN","value"]="true"
array["PREFIX_ORDERED","value"]="true"
array["PREFIX_ARRAY","value"]="true"

for item in "${!array[@]}"; do
    echo "${item}"
done

Desired output is:

PREFIX_THIS,value
PREFIX_IS,value
PREFIX_AN,value
PREFIX_ORDERED,value
PREFIX_ARRAY,value

But I'm obtaining this:

PREFIX_IS,value
PREFIX_ORDERED,value
PREFIX_THIS,value
PREFIX_AN,value
PREFIX_ARRAY,value

Until here the first problem. For the second problem, the order is not important. I added more stuff to the associative array and I just want to loop on it without duplicates. Adding this:

array["PREFIX_THIS","text"]="Text for the var"
array["PREFIX_IS","text"]="Another text"
array["PREFIX_AN","text"]="Text doesn't really matter"
array["PREFIX_ORDERED","text"]="Whatever"
array["PREFIX_ARRAY","text"]="More text"

I just want to loop over "PREFIX_THIS", "PREFIX_IS", "PREFIX_AN", etc... printing each one only once. I just want to print doing an "echo" on loop (order is not important for this part, just to print each one only once). Desired output:

PREFIX_ORDERED
PREFIX_AN
PREFIX_ARRAY
PREFIX_IS
PREFIX_THIS

I achieved it doing "dirty" stuff. But there must be a more elegant way. This is my working but not too much elegant approach:

already_set=""
var_name=""
for item in "${!array[@]}"; do
    var_name="${item%,*}"
    if [[ ! ${already_set} =~ "${var_name}" ]]; then
        echo "${var_name}"
        already_set+="${item}"
    fi
done

Any help? Thanks.

Upvotes: 2

Views: 2610

Answers (1)

Socowi
Socowi

Reputation: 27215

Iteration Order

As Inian pointed out in the comments, you cannot fix the order in which "${!array[@]}" expands for associative arrays. However, you can store all keys inside a normal array that you can order manually.

keysInCustomOrder=(PREFIX_{THIS,IS,AN,ORDERED,ARRAY})
for key in "${keysInCustomOrder[@]}"; do
    echo "do something with ${array[$key,value]}"
done

Unique Prefixes of Keys

For your second problem: a["key1","key2"] is the same as a["key1,key2"]. In bash, arrays are always 1D therefore there is no perfect solution. However, you can use the following one-liner as long as , is never part of key1.

$ declare -A array=([a,1]=x [a,2]=y [b,1]=z [c,1]=u [c,2]=v)
$ printf %s\\n "${!array[@]}" | cut -d, -f1 | sort -u
a
b
c

When your keys may also contain linebreaks delemit each key by null \0.

printf %s\\0 "${!array[@]}" | cut -zd, -f1 | sort -zu

Alternatively you could use reference variables to simulate 2D-arrays, however I would advice against using them.

Upvotes: 2

Related Questions