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.
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;
}
This isn't included for a couple of reasons:
NSDictionary uses hash equality, and for pretty much any good hashing algorithm, any variation in the source string results in a different hash.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With