Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extend Dictionary where Key is of type String

I want to have extend a method of Dictionary but only if Key is of type String.

I try doing this:

extension Dictionary where Key: String {
    mutating func lowercaseKeys() {
        for key in self.keys {
            self[key.lowercase] = self.removeValueForKey(key)
        }
    }
}

And get error:

Type 'Key' constrained to non-protocol type 'String'

Based on this error message I can tell that I can only do this kind of filtering with protocols... Is there a way to by-pass this?

like image 234
Arbitur Avatar asked Oct 16 '15 21:10

Arbitur


2 Answers

I believe the closest protocol that meets your needs is StringLiteralConvertible, which, with a few extra lines, will let you accomplish this

extension Dictionary where Key: StringLiteralConvertible {
    mutating func setAllKeysLowercase() {
        for key in self.keys {
            if let lowercaseKey = String(key).lowercaseString as? Key {
                self[lowercaseKey] = self.removeValueForKey(key)
            }
        }
    }
}

var stringKeyDictionary = [ "Hello" : NSObject(), "World" : NSObject() ]
stringKeyDictionary.setAllKeysLowercase()
print( stringKeyDictionary )

// Prints: ["hello": <NSObject: 0x1007033c0>, "world": <NSObject: 0x1007033d0>]

var numberKeyDictionary = [ 0 : NSObject(), 1: NSObject() ]
numberKeyDictionary.setAllKeysLowercase() //< Won't compile, keys are not strings
like image 142
Patrick Lynch Avatar answered Oct 06 '22 20:10

Patrick Lynch


I've updated this for Swift 4.

extension Dictionary where Key: StringProtocol {
    mutating func setAllKeysLowercase() {
        for key in self.keys {
            if let lowercaseKey = key.lowercased() as? Key {
                self[lowercaseKey] = self.removeValue(forKey: key)
            }
        }
    }
}

var stringKeyDictionary = [ "Hello" : 123, "World" : 456 ]
stringKeyDictionary.setAllKeysLowercase()
print( stringKeyDictionary )

Prints: ["hello": 123, "world": 456]

var numberKeyDictionary = [ 0 : 123, 1: 456 ]
numberKeyDictionary.setAllKeysLowercase()

gives error: error: cannot use mutating member on immutable value: 'numberKeyDictionary' is immutable

Since StringLiteralConvertible is deprecated I thought it might be better to use StringProtocol instead of the Swift4 equivalent ExpressibleByStringLiteral. Using ExpressibleByStringLiteral means the Key isn't a String so can't use String methods like lowercased.

like image 2
Bob Peterson Avatar answered Oct 06 '22 19:10

Bob Peterson