Swift中不区分大小写的字典

Cod*_*ent 7 swift swift2

给定一个DictionaryKey的类型是String,有没有在不区分大小写的方式来访问值的方法吗?例如:

let dict = [
    "name": "John",
    "location": "Chicago"
]
Run Code Online (Sandbox Code Playgroud)

有没有办法打电话dict["NAME"], dict["nAmE"]等等"John"

max*_*lov 7

更清洁的方法,快速4:

extension Dictionary where Key == String {

    subscript(caseInsensitive key: Key) -> Value? {
        get {
            if let k = keys.first(where: { $0.caseInsensitiveCompare(key) == .orderedSame }) {
                return self[k]
            }
            return nil
        }
        set {
            if let k = keys.first(where: { $0.caseInsensitiveCompare(key) == .orderedSame }) {
                self[k] = newValue
            } else {
                self[key] = newValue
            }
        }
    }

}

// Usage:
var dict = ["name": "John"]
dict[caseInsensitive: "NAME"] = "David" // overwrites "name" value
print(dict[caseInsensitive: "name"]!) // outputs "David"
Run Code Online (Sandbox Code Playgroud)

  • 复杂度也从 O(1) 上升到 O(n)。那么也可以使用键值数组。下面的解决方案可以更好地解决这个问题。 (2认同)

Cod*_*ent 6

Swift支持多个下标,因此您可以利用它来定义不区分大小写的访问者:

extension Dictionary where Key : StringLiteralConvertible {
    subscript(ci key : Key) -> Value? {
        get {
            let searchKey = String(key).lowercaseString
            for k in self.keys {
                let lowerK = String(k).lowercaseString
                if searchKey == lowerK {
                    return self[k]
                }
            }
            return nil
        }
    }
}

// Usage:
let dict = [
    "name": "John",
    "location": "Chicago",
]

print(dict[ci: "NAME"])      // John
print(dict[ci: "lOcAtIoN"])  // Chicago
Run Code Online (Sandbox Code Playgroud)

此扩展名仅限Dictionary于其Key类型String(因为小写对其他数据类型毫无意义).但是,Swift会抱怨将泛型类型约束为a struct.最接近的协议StringStringLiteralConvertible.

请注意,如果您有两个小写形式相同的键,则无法保证您将获得哪一个:

let dict = [
    "name": "John",
    "NAME": "David",
]

print(dict[ci: "name"])   // no guarantee that you will get David or John.
Run Code Online (Sandbox Code Playgroud)

  • 在反序列化JSON期间,将键标准化为类似小写的内容. (2认同)

Wer*_*her 6

现有的答案很好,但是使用这些策略查找/插入的时间复杂度从 O(1) 恶化到 O(N)(其中 N 是字典中的对象数)。

要保留 O(1),您可能需要考虑以下方法:

/// Wrapper around String which uses case-insensitive implementations for Hashable
public struct CaseInsensitiveString: Hashable, LosslessStringConvertible, ExpressibleByStringLiteral {
    public typealias StringLiteralType = String

    private let value: String
    private let caseInsensitiveValue: String

    public init(stringLiteral: String) {
        self.value = stringLiteral
        self.caseInsensitiveValue = stringLiteral.lowercased()
    }

    public init?(_ description: String) {
        self.init(stringLiteral: description)
    }

    public var hashValue: Int {
        return self.caseInsensitiveValue.hashValue
    }

    public static func == (lhs: CaseInsensitiveString, rhs: CaseInsensitiveString) -> Bool {
        return lhs.caseInsensitiveValue == rhs.caseInsensitiveValue
    }

    public var description: String {
        return value
    }
}

var dict = [CaseInsensitiveString: String]()
dict["name"] = "John"
dict["NAME"] = "David" // overwrites "name" value
print(dict["name"]!) // outputs "David"
Run Code Online (Sandbox Code Playgroud)