【发布时间】:2015-10-17 02:58:55
【问题描述】:
给定一个Dictionary,其Key 的类型为String,有没有办法以不区分大小写的方式访问该值?例如:
let dict = [
"name": "John",
"location": "Chicago"
]
有没有办法打电话dict["NAME"], dict["nAmE"]等,仍然得到"John"?
【问题讨论】:
给定一个Dictionary,其Key 的类型为String,有没有办法以不区分大小写的方式访问该值?例如:
let dict = [
"name": "John",
"location": "Chicago"
]
有没有办法打电话dict["NAME"], dict["nAmE"]等,仍然得到"John"?
【问题讨论】:
更简洁的方法,swift 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"
【讨论】:
var dict = ["name": "John", "NAME": "David"] 并且不区分大小写的查找结果将变得不可预测。
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
此扩展仅限于Dictionary,其Key 的类型为String(因为小写对其他数据类型没有意义)。但是,Swift 会抱怨将泛型类型限制为struct。最接近String 的协议是StringLiteralConvertible。
请注意,如果您有 2 个小写形式相同的键,则无法保证您会返回哪一个:
let dict = [
"name": "John",
"NAME": "David",
]
print(dict[ci: "name"]) // no guarantee that you will get David or John.
【讨论】:
现有的答案很好,但是使用这些策略进行查找/插入的时间复杂度从 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"
【讨论】:
可以使用Collection's first(where:)从所有映射为小写的键中找到第一个小写匹配,然后从这个结果中返回值。
extension Dictionary where Key == String {
func valueForKeyInsensitive<T>(key: Key) -> T? {
let foundKey = self.keys.first { $0.compare(key, options: .caseInsensitive) == .orderedSame } ?? key
return self[foundKey] as? T
}
}
first(where:) 是过滤或迭代大型集合的一种非常有效的方法
参考:
【讨论】:
这应该使用 O(1) 完成这项工作,同时也不允许添加具有不同大小写的相同字符串(例如,如果您首先插入 Def 它不会被 DEF 替换)。如有必要,它也适用于 Substring。请注意,此解决方案更节省内存,但代价是在每次查找字符串时重新计算字符串转换和散列。如果您需要经常查找相同的值,则可能值得拥有一个缓存 hashValue 的实现。
struct CaseInsensitiveString<T: StringProtocol>: Hashable, Equatable, CustomStringConvertible {
var string: T
init(_ string: T) {
self.string = string
}
var description: String { get {
return string.description
}}
var hashValue: Int { get {
string.lowercased().hashValue
} }
func hash(into hasher: inout Hasher) {
hasher.combine(hashValue)
}
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.string.compare(rhs.string, options: .caseInsensitive) == .orderedSame
}
}
typealias SubstringCI = CaseInsensitiveString<String>
var codeMap = [SubstringCI: Int]()
let test = "Abc Def Ghi"
let testsub = test[test.firstIndex(of: "D")!...test.lastIndex(of: "f")!]
codeMap[SubstringCI(String(testsub))] = 1
print(codeMap.keys, codeMap[SubstringCI("Def")]!, codeMap[SubstringCI("def")]!)
codeMap[SubstringCI("DEF")] = 1
print(codeMap.keys, codeMap[SubstringCI("Def")]!, codeMap[SubstringCI("def")]!)
【讨论】: