Sample Header Ad - 728x90

Inverting an associative array

10 votes
4 answers
2419 views
Let's say I have an associative array in bash, declare -A hash hash=( ["foo"]=aa ["bar"]=bb ["baz"]=aa ["quux"]=bb ["wibble"]=cc ["wobble"]=aa ) where both keys and values are unknown to me (the actual data is read from external sources). How may I create an array of the keys corresponding to the same value, so that I may, in a loop over all unique values, do printf 'Value "%s" is present with the following keys: %s\n' "$value" "${keys[*]}" and get the output (not necessarily in this order) Value "aa" is present with the following keys: foo baz wobble Value "bb" is present with the following keys: bar quux Value "cc" is present with the following keys: wibble The important bit is that the keys are stored as separate elements in the keys array and that they therefore do not need to be parsed out of a text string. I could do something like declare -A seen seen=() for value in "${hash[@]}"; do if [ -n "${seen[$value]}" ]; then continue fi keys=() for key in "${!hash[@]}"; do if [ "${hash[$key]}" = "$value" ]; then keys+=( "$key" ) fi done printf 'Value "%s" is present with the following keys: %s\n' \ "$value" "${keys[*]}" seen[$value]=1 done But it seems a bit inefficient with that double loop. Is there a piece of array syntax that I've missed for bash? Would doing this in e.g. zsh give me access to more powerful array manipulation tools? In Perl, I would do my %hash = ( 'foo' => 'aa', 'bar' => 'bb', 'baz' => 'aa', 'quux' => 'bb', 'wibble' => 'cc', 'wobble' => 'aa' ); my %keys; while ( my ( $key, $value ) = each(%hash) ) { push( @{ $keys{$value} }, $key ); } foreach my $value ( keys(%keys) ) { printf( "Value \"%s\" is present with the following keys: %s\n", $value, join( " ", @{ $keys{$value} } ) ); } But bash associative arrays can't hold arrays... I'd also be interested in any old school solution possibly using some form of indirect indexing (building a set of index array(s) when reading the values that I said I had in hash above?). It feels like there ought to be a way to do this in linear time.
Asked by Kusalananda (354278 rep)
Mar 17, 2019, 10:48 PM
Last activity: May 22, 2024, 05:28 PM