NSDictionary 有 objectForKey 但它的键区分大小写。有没有功能可用,如
- (id)objectForKey:(id)aKey options:(id) options;
在选项中您可以传递“NSCaseInsensitiveSearch”
要从不区分大小写的 NSDictionary 获取密钥,可以使用下面编写的代码。
您需要添加具有此功能的 NSDictionary 类的类别
- (id)objectForCaseInsensitiveKey:(NSString *)key {
NSArray *allKeys = [self allKeys];
for (NSString *str in allKeys) {
if ([key caseInsensitiveCompare:str] == NSOrderedSame) {
return [self objectForKey:str];
}
}
return nil;
}
不包括在内有几个原因:
NSDictionary 使用哈希相等,对于几乎任何好的哈希算法,源字符串中的任何变化都会导致不同的哈希。
更重要的是,NSDictionary 键不是字符串。任何符合 NSCopying 的对象都可以是字典键,并且它包含的不仅仅是字符串。 NSNumber 与 NSBezierPath 的不区分大小写的比较会是什么样子?
这里的许多答案都提供了将字典转换为数组并对其进行迭代的解决方案。这很有效,如果您只是一次性需要它,那也没关系。但该解决方案有点丑陋,并且性能特征很差。如果这是我非常需要的东西(例如,足以创建 NSDictionary 类别),我希望在数据结构级别正确解决它。
您想要的是一个包装 NSDictionary 的类,只允许使用字符串作为键,并在给定键时自动将键小写(如果您需要双向映射,还可能记住原始键)。这实现起来相当简单,并且是一个更简洁的设计。对于一次性来说它太重了,但如果这是你经常做的事情,我认为值得干净地做。
正确的答案是你应该使用大小写折叠键作为字典键。 这与将它们转换为大写或小写不同,并且不会破坏 O(1) 平均情况搜索/插入复杂度。
不幸的是,Cocoa 似乎没有合适的
NSString
方法来对字符串进行大小写折叠,但 Core Foundation 有 CFStringFold()
可以用于此目的。让我们编写一个简短的函数来完成必要的工作:
NSString *foldedString(NSString *s, NSLocale *locale)
{
CFMutableStringRef ret = CFStringCreateMutableCopy(kCFAllocatorDefault, 0,
(__bridge CFStringRef)s);
CFStringNormalize(ret, kCFStringNormalizationFormD);
CFStringFold(ret, kCFCompareCaseInsensitive, (__bridge CFLocaleRef)locale);
return (__bridge_transfer NSString *)ret;
}
注意,locale参数很重要。如果您指定
NULL
,您将获得当前系统区域设置。这在大多数情况下都没有问题,但土耳其用户可能会惊讶于“I”匹配“i”而不是“ı”。因此,您可能想要传递[NSLocale currentLocale]
,如果您要保存结果,您可能还想保存区域设置标识符并从中创建区域设置。
所以,当添加到字典时,你现在需要做
[dict setObject:obj forKey:foldedString(myKey, locale)];
并再次抬头看
[dict objectForKey:foldedString(myKey, locale)];
最后一个观察结果是,您可能希望将大小写折叠的键与原始值一起存储,这样您就不必在每次访问字典时都折叠它们。
在下面编写的代码中,我搜索输入键的实际键。所以,如果输入 key=@"naMe" 那么实际的 key=@"name"。
NSDictionary *dic=[NSDictionary dictionaryWithObjectsAndKeys:@"John",@"Name",@"123456",@"empId", nil];
NSString *key=@"naMe";
NSString *name=[dic objectForKey:key];
if(name==nil){
NSPredicate *searchPred=[NSPredicate predicateWithFormat:@"self LIKE[cd] %@",key];
NSArray *searchedKeys=[[dic allKeys] filteredArrayUsingPredicate:searchPred];
if(searchedKeys.count>0){
name=[dic objectForKey:[searchedKeys objectAtIndex:0]];
}
}
NSLog(@"Name = %@",name);
许多答案都是正确的,但这里有一个更多的例子:
NSDictionary* dict= @{ @"hello" : @"Hey" };
NSArray* keys= [dict allKeys];
NSUInteger index=[keys indexOfObjectPassingTest: ^BOOL (id obj, NSUInteger index, BOOL* stop)
{
if( [obj caseInsensitiveCompare: @"Hello"]==NSOrderedSame)
{
*stop= YES;
return YES;
}
else
{
return NO;
}
}];
我个人觉得这种方法比较简单,但是每个人都有自己的编程风格。
编辑
一个可读性较差但更短的解决方案:
NSDictionary* dict= @{ @"hello" : @"Hey" };
NSArray* keys= [dict allKeys];
NSUInteger index=[keys indexOfObjectPassingTest: ^BOOL (id obj, NSUInteger index, BOOL* stop)
{
return *stop= [obj caseInsensitiveCompare: @"Hello"]==NSOrderedSame ;
}];
如果您仅在一个位置(可能是两个或三个)的 NSDictionary 中存储和检索,则可以使用
[myString 小写字符串]
两者皆有。如果在整个代码中使用字典对象,则更严格的答案会很有用。
我认为最好的解决方案是创建
NSString
的子类,其 isEqual:
和 hash
函数不区分大小写。我想知道为什么这不是答案。不过,我自己还没有尝试过。如果有人有,请更新这个答案 - 我让它可供每个人编辑(“社区维基”)。