Mark Reed
Mark Reed

Reputation: 95267

Find key that matches value in zsh associative array?

In a regular array, I can use (i) or (I) to search for the index of entries matching a given value (first match from the start or end of the array, respectively):

list=(foo bar baz)
echo $list[(i)bar]
# => 2

This doesn't work for associative arrays, to get (one of) the key(s) where a value is found:

declare -A hash=([foo]=bar [baz]=zoo)
echo $hash[(i)bar]
# => no output 

Is there another mechanism for doing this, other than manually looping through?

Upvotes: 5

Views: 1669

Answers (2)

Gairfowl
Gairfowl

Reputation: 2981

The (r) subscript flag combined with the (k) parameter flag should return what you want:

declare -A hash=([foo]=bar [baz]=zoo)
echo ${(k)hash[(r)bar]}
# => foo

The man page section on the (r) subscript flag only talks about returning values and ignores this usage, so it's hard to find.


The (r) subscript flag will return the 'first' matching entry (associative arrays have no defined order, so 'first' can vary based on the whims of the implementation). To get all of the matches, use the (R) subscript flag:

typeset -A hash=([foo]=false [baz]=true [bar]=false)
typeset -a keys=( ${(k)hash[(R)false]} )
typeset -p keys
# => typeset -a keys=( foo bar )

Upvotes: 7

chepner
chepner

Reputation: 531275

Here is something completely disgusting:

% declare -A hash=([foo]=bar [baz]=zoo)
% echo ${${(kA)hash}[${${(A)hash[@]}[(i)bar]}]}
foo

Basically, it consists of two parts:

  1. ${${(A)hash[@]}[(i)bar]}, which computes the index of bar in an anonymous array consisting of the values of the associative array.
  2. ${${(kA)hash}[...]}, which indexes the anonymous array consisting of the keys of the associative array using the numerical index computed by the previous expansion.

I'm not aware of a short equivalent to the I flag, and I too am surprised that the seemingly obvious extension to associative arrays doesn't exist.

Upvotes: 1

Related Questions