【问题标题】:NSDictionary case insensitive objectForKey:NSDictionary 不区分大小写的 objectForKey:
【发布时间】:2012-11-28 14:26:15
【问题描述】:

NSDictionary 有 objectForKey 但它对键是区分大小写的。有 没有 功能,如

- (id)objectForKey:(id)aKey options:(id) options;

你可以在哪里传递“NSCaseInsensitiveSearch”

要从不区分大小写的 NSDictionary 中获取密钥,可以使用下面编写的以下代码。

【问题讨论】:

  • 有正当理由的反对票。
  • 这是一个自己回答和分享的问题。
  • 但是,当我提出问题时,我看到了回答您自己的问题的选项。所以,这就是我所做的。顺便说一句,您对获得声誉的评论非常讽刺。我看到很多编码风格,您也可以尝试像@Ramy 那样回答。请注明你的博客,这样我就可以在那里问这样的问题。
  • @Jennis:可能是,也可能不是。如果你有一个好的问题并找到了一个好的答案,这就是 Stack Overflow 的用途。
  • @Jennis 我想分享我的答案。我发现“回答你自己的问题——分享你的知识,问答式”。所以我写了这一行“要从不区分大小写的 NSDictionary 中获取密钥,可以使用下面编写的以下代码。”很奇怪你以这种方式很好地附上了这个网站的回复。请查看许多问题,人们已经回答了自己的问题以帮助他人。无论如何,感谢您的批评。

标签: objective-c ios nsdictionary key-value case-insensitive


【解决方案1】:

您需要添加具有此功能的 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;
}

【讨论】:

  • 创建一个类别是个好主意。但是,为什么你需要写一个 for 循环。您可以使用使用 NSPredicate 的现成函数。
  • ...您只是将搜索复杂性从 O(log(N))) 提高到了 O(N)
  • @ivanzoid 实际上,你只是将它从O(1) 撞到了O(n)NSDictionary 是哈希表,而不是树。
【解决方案2】:

这不包括在内有几个原因:

  1. NSDictionary 使用哈希相等,对于几乎任何好的哈希算法,源字符串的任何变化都会导致不同的哈希。

  2. 更重要的是,NSDictionary 键不是字符串。任何符合 NSCopying 的对象都可以是一个字典键,它包含的内容远不止字符串。 NSNumber 与 NSBezierPath 的不区分大小写比较是什么样的?

这里的许多答案都提供了相当于将字典转换为数组并对其进行迭代的解决方案。那行得通,如果您只需要一次性使用它,那很好。但是该解决方案有点丑陋并且具有不良的性能特征。如果这是我非常需要的东西(比如说,足以创建一个 NSDictionary 类别),我想在数据结构级别正确解决它。

你想要的是一个包装 NSDictionary 的类,只允许字符串作为键,并在给定键时自动小写键(如果你需要双向映射,还可能记住原始键)。这将是相当简单的实现,并且是一个更清洁的设计。它对于一次性来说太重了,但如果这是你经常做的事情,我认为值得干净地做。

【讨论】:

  • 完美,这是一个非常好的答案。我同意你的观点 objectForKey 不是 NSString 总是,但这是最常用的。我认为当调用 objectForKey 时会发生这种情况,系统计算传递给函数的键的哈希值,然后执行 for 循环以将该哈希值与可用哈希值匹配,并在找到匹配项时返回值。如有错误请指正。
  • @andyPaul:比这要复杂一点。该算法将花费线性时间来查找键的值(即字典中的每个附加项都会增加一定量的开销,这使得匹配键和值平均需要更长的时间),但 NSDictionary 即使有数十万个也能保持快速的对象。但是核心思想,它通过比较哈希来工作,是完全正确的。 (顺便说一句,我并不是为了难而含糊。NSDictionary 不是一个类,而是许多具有不同实现的不同类,从外面看都是一样的。)
  • 小写不是正确的转换——你需要大小写折叠。
【解决方案3】:

正确的答案是您应该使用大小写折叠键作为字典键。 这与将它们转换为大写或小写不同,它不会破坏 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],如果您要保存结果,您可能还希望保存语言环境标识符并从中创建语言环境。 p>

所以,当添加到字典时,你现在需要做的

[dict setObject:obj forKey:foldedString(myKey, locale)];

然后再抬头

[dict objectForKey:foldedString(myKey, locale)];

最后一个观察结果是,您可能希望将大小写折叠的键与原始值一起存储,这样您就不必在每次访问字典时都折叠它们。

【讨论】:

    【解决方案4】:

    在下面编写的代码中,我搜索输入键的实际键。所以,如果 input 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);
    

    【讨论】:

    • 你也可以这样做。 stackoverflow.com/questions/6135000/…
    • 这是复杂而缓慢的;它首先构造一个包含字典O(n) 中所有键的数组,然后必须解析NSPredicate 并过滤结果数组。
    【解决方案5】:

    很多答案都是正确的,但这里有一个更多的例子:

        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 ;
    
         }];
    

    【讨论】:

    • 看看你写的代码的行数。但是,是的,您可以将整个块写在一行中,但会非常难以理解。
    • 行数很多,但代码量很少。我不会返回 [obj caseInsensitiveCompare: @"Hello"]==NSOrderedSame 只是因为在返回值之前我必须将 *stop 设置为 YES,这样单行就会像你说的那样,难以阅读。
    • 这是另一个O(n) 答案。 NSDictionary 访问是 O(1)。我想至少它没有使用NSPredicate
    【解决方案6】:

    如果你只是在一个地方(可能是两个或三个)存储和检索 NSDictionary,你可以使用

    [myString 小写字符串]

    两者都有。如果在整个代码中都使用字典对象,则更严格的答案会很有用。

    【讨论】:

    • 这实际上比上面大多数较长的答案要好得多。唯一的错误是小写不会在所有情况下都按预期工作(你真的应该对字符串进行大小写折叠)。
    猜你喜欢
    • 1970-01-01
    • 2012-12-01
    • 2013-03-06
    • 2020-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多