NSDictionary case insensitive objectForKey:

巧了我就是萌 提交于 2020-12-08 06:04:53

问题


NSDictionary has objectForKey but it's case-sentive for keys. There is No function available like

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

where in options you can pass "NSCaseInsensitiveSearch"

To get key's from NSDictionary which is case-insesitive one can use the following code written below.


回答1:


This isn't included for a couple of reasons:

  1. NSDictionary uses hash equality, and for pretty much any good hashing algorithm, any variation in the source string results in a different hash.

  2. More importantly, NSDictionary keys are not strings. Any object that conforms to NSCopying can be a dictionary key, and that includes a whole lot more than strings. What would a case-insensitive comparison of an NSNumber with an NSBezierPath look like?

Many of the answers here offer solutions that amount to transforming the dictionary into an array and iterating over it. That works, and if you just need this as a one-off, that's fine. But that solution is kinda ugly and has bad performance characteristics. If this were something I needed a lot (say, enough to create an NSDictionary category), I would want to solve it properly, at the data structure level.

What you want is a class that wraps an NSDictionary, only allows strings for keys and automatically lowercases keys as they are given (and possibly also remembers the original key if you need a two-way mapping). This would be fairly simple to implement and is a much cleaner design. It's too heavy for a one-off, but if this is something you're doing a lot, I think it's worth doing cleanly.




回答2:


You need to add Category of NSDictionary Class with this functionality

- (id)objectForCaseInsensitiveKey:(NSString *)key {
    NSArray *allKeys = [self allKeys];
    for (NSString *str in allKeys) {
        if ([key caseInsensitiveCompare:str] == NSOrderedSame) {
            return [self objectForKey:str];
        }
    }
    return nil;
}



回答3:


In the code written below, I search for a actual key for a input key. So , if input key=@"naMe" then the actual 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);



回答4:


The correct answer is that you should use case-folded keys as dictionary keys. This is not the same as converting them to upper or lower case and it won't destroy the O(1) average case search/insert complexity.

Unfortunately, Cocoa doesn't seem to have an appropriate NSString method to case-fold a string, but Core Foundation has CFStringFold() which you can use for that purpose. Let's write a short function to do the necessary work:

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

Note that the locale argument is important. If you specify NULL, you will get the current system locale. This will be fine in most cases, but Turkish users might be surprised that "I" matches "i" rather than "ı". You might therefore want to pass [NSLocale currentLocale], and if you're saving the results you might also want to save the locale identifier and create the locale from that.

So, when adding to the dictionary, you now need to do

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

and to look up again

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

One final observation is that you might wish to store the case-folded keys alongside the original values, then you don't have to fold them on every access to the dictionary.




回答5:


Many answers are correct, but here's a more example:

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

Personally I find this method easier, but everyone has his programming style.

EDIT

A less readable but shorter solution:

    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 ;

     }];



回答6:


If you are only storing into, and retrieving from, the NSDictionary in one place (maybe two or three), you could use

[myString lowercaseString]

in both. The more rigorous answers are useful if the dictionary object is used all over your code.



来源:https://stackoverflow.com/questions/13607343/nsdictionary-case-insensitive-objectforkey

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!