Search code examples
swiftdictionaryiterationgeneric-collections

Iterate through a generically-typed Swift Dictionary with conditional sorting


I have two versions of a loop over a generic Dictionary collection in Swift. I am not happy about the refactored version because it has the overhead of creating a new Array object out of the LazyBidirectionalCollection returned by dict.keys.

On the other hand it's possible there is no real extra overhead and I doth protest too much. On the third hand, I'd love to understand this deeply enough that I know a) one cannot avoid creating the Array or b) there's a way but it may have other drawbacks.

func dump1<Key, Val where Key: Hashable, Key: Comparable>(dict: [Key: Val], sort: Bool = true) -> String {
    var d = ""

    if sort {
        for k in sorted(dict.keys, {$0 < $1}) {
            d += "\(k): \(dict[k]!)\n"
        }
    }
    else {
        for k in dict.keys {
            d += "\(k): \(dict[k]!)\n"
        }
    }
    return d
}


func dump2<Key, Val where Key: Hashable, Key: Comparable>(dict: [Key: Val], sort: Bool = true) -> String {
    var d = ""

    var keys = sort ? sorted(dict.keys, {$0 < $1}) : Array(dict.keys)
    for k in keys {
        d += "\(k): \(dict[k]!)\n"
    }
    return d
}

Solution

  • Thanks to Mr. AirSpeed (http://airspeedvelocity.net/2014/07/28/collection-and-sequence-helpers/), type erasure is what I was missing:

    func dumpIt<Key, Val where Key: Hashable, Key: Comparable>(dict: [Key: Val], sort: Bool = true) -> String {
        var printOut = ""
        for k in sort ? SequenceOf(sorted(dict.keys) {$0 < $1}) : SequenceOf(dict.keys) {
            printOut += "\(k): \(dict[k]!) "
        }
        return printOut
    }
    

    Although a follow-up question is, if I want to use reduce rather than a for-loop, but I still don't want to create an Array, how can I create a type-erased Collection view on the above sequences.

    Anyway, if the array version is just as efficient, this is probably preferable notation:

    func dumpIt2<Key, Val where Key: Hashable, Key: Comparable>(dict: [Key: Val], sort: Bool = true) -> String {
        return (sort ? sorted(dict.keys) {$0 < $1} : Array(dict.keys)).reduce("") { $0 + "\($1): \(dict[$1]!) " }
    }