Add some kind of .firstKey(withValue:) to Dictionaries?

Searching dictionary values instead of dictionary keys, in my opinion, is a fairly common and reasonable operation to perform on a dictionary. For instance, if I had some dictionary contacts that held people's names as keys and their phone numbers as values, I'd most commonly want to look up people's phone numbers, but it's not inconceivable that I'd often want to look up the name for a given phone number as well. It can be done, such as with the following implementation:

myDictionary.first(where: {$1==someValue})?.key

But the readability and conciseness here is really quite low. We could also initialize some dictionary with the values and the keys swapped, then search that:

var swappedDictionary = Dictionary(grouping: myDictionary.keys, by: {myDictionary[$0]! })

swappedDictionary[someValue]! //access key to correspond with value

but while this makes it much more readable and concise to access after the swapped dictionary is created, the creation of the swapped dictionary itself is even less readable than the above, and it won't update if elements are added/removed/modified in the original dictionary. Ultimately, the best solutions when this problem is to implement one of these as an extension of Collection, which suggests to me that firstKey(withValue:) should really just be an included method in the first place. Some kind of allKeys(withValue:) might be warranted as a possible substitute instead.

1 Like

I think this example is a bit easier to understand at a glance if you use $0.value instead of $1, but this may be a style preference:

myDictionary.first(where: { $0.value == someValue })?.key

Either way, with firstKey(withValue:), this would become:

myDictionary.firstKey(withValue: someValue)

This seems like a nice legibility improvement for this specific case, but a key difference is that firstKey(withValue:) takes a specific value while first(where:) takes a closure. This means that firstKey(withValue:) is less expressive than the existing first(where:) method, which probably limits its usefulness.

For example, take this first(where:):

myDictionary.first(where: { $0.value.isSomeValue })?.key

converting this to a firstKey pattern would require a spelling like:

myDictionary.firstKey(whereValueMatches: { $0.isSomeValue })

and at that point, I don't think firstKey(whereValueMatches:) really has any ergonomic benefits over the existing first(where:) method.

1 Like