如果Dictionary
的{{1}}类型为Key
,是否有办法以不区分大小写的方式访问该值?例如:
String
有没有办法打电话给let dict = [
"name": "John",
"location": "Chicago"
]
等,并且获得dict["NAME"], dict["nAmE"]
?
答案 0 :(得分:6)
Swift支持多个下标,因此您可以利用它来定义不区分大小写的访问者:
"John"
此扩展程序仅限于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
(因为小写对其他数据类型毫无意义)。但是,Swift会抱怨将泛型类型限制为String
。最接近struct
的协议是String
。
请注意,如果您有两个小写形式相同的键,则无法保证您将获得哪一个:
StringLiteralConvertible
答案 1 :(得分:4)
更清洁的方法,快速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"
答案 2 :(得分:1)
现有答案很好,但是采用这些策略的查找/插入的时间复杂度从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"
答案 3 :(得分:0)
可以使用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:)
是过滤或迭代大型集合的一种非常有效的方法
参考:
答案 4 :(得分:0)
这应该在使用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")]!)