Should Dictionary support first related methods from Collection conformance?

The combination of first and dropFirst() is sometimes used in “reduce-style” algorithms which need to visit every element of a collection but are not sensitive to the order of elements. These algorithms work just fine with Dictionary since first will return the same element dropFirst() removes as long as you don’t mutate the collection between the two calls.

For instance, consider this implementation of max():

extension Collection {
    func myMax(
        by areInIncreasingOrder: (Element, Element) throws -> Bool
    ) rethrows -> Element? {
        guard var result = first else { return nil }
        for elem in dropFirst() where areInIncreasingOrder(result, elem) {
            result = elem
        }
        return result
    }
}

It works just fine on a Dictionary:

print(
    “key of largest value:”,
    someDictionary.myMax(by: { $0.value < $1.value })?.key
)

randomElement() would not work here because there is no equivalent to dropFirst() for it. Moreover, even if we did have a dropRandomElement() which returned the elements the previous randomElement() hadn’t, or some other way to split a Dictionary into first and rest with a random “first element”, we would be giving up performance for no good reason—generating a random number is relatively slow and it buys us nothing here.

I’ve shown this as an extension on Collection, but it would make perfect sense in an extension directly on Dictionary, too. In fact, I’d argue that there is no better implementation for it on Dictionary than this one.

Sometimes you just want to get the first element the iteration will return to you, no matter what arbitrary order the iteration might be in. That’s what first is for, and it’s a perfectly coherent thing to want even on a Dictionary.

10 Likes