2012-11-28 50 views
6

NSDictionary具有objectForKey,但它對於鍵來說是大小寫的。有沒有功能可以像NSDictionary不區分大小寫objectForKey:

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

其中選項,你可以通過「NSCaseInsensitiveSearch」

從NSDictionary中獲得關鍵的是區分insesitive一個可以使用下面寫了下面的代碼。

+0

以正當理由歡迎倒票。 – andyPaul

+0

它是一個自我回答和分享的問題。 – andyPaul

+0

但是,當我問一個問題時,我看到了回答你自己的問題的選項。所以,那就是我所做的。順便說一下,你對獲得聲望的評論頗爲諷刺。我看到很多編碼風格,你也可以嘗試像@Ramy那樣回答。請指定您的博客,以便我可以在那裏提出這些問題。 – andyPaul

回答

8

這不包括了幾個方面的原因:

  1. 的NSDictionary使用散列平等,和用於幾乎沒有任何好的散列算法,源字符串中的任何變體都會導致不同的散列。

  2. 更重要的是,NSDictionary鍵不是字符串。任何符合NSCopying的對象都可以是一個字典關鍵字,並且包含比字符串更多的東西。 NSNumber與NSBezierPath的大小寫不敏感比較是什麼樣的?

這裏提供的許多答案都提供了將字典轉換爲數組並迭代它的解決方案。這是有效的,如果你只是需要這個一次性,那很好。但是這個解決方案有點醜,並且性能不好。如果這是我需要的很多東西(比如說,足夠創建一個NSDictionary類),我想在數據結構級別正確解決它。

你想要的是一個包裝NSDictionary的類,它只允許鍵的字符串,並在給定的時候自動降低鍵值(如果需要雙向映射的話也可以記住原始鍵值)。這將是相當簡單的實施,是一個更清潔的設計。這對一次性來說太重了,但如果這是你做了很多事情,我認爲這是值得幹乾淨淨的。

+0

完美,這是一個非常好的答案。我同意你的觀點objectForKey不總是NSString,但這是最常用的。我認爲當調用objectForKey時會發生什麼,系統會計算傳遞給該函數的鍵的哈希值,然後執行for循環以將該哈希值與可用哈希值進行匹配,並在找到匹配項時返回該值。如果我錯了,請糾正我。 – andyPaul

+0

@andyPaul:比這更復雜一點。該算法會花費線性時間爲關鍵字找到一個值(即字典中的每個附加項都會增加一定的開銷,這使得平均需要更長的時間才能匹配鍵和值),但NSDictionary保持快速,即使數十萬的物體。但核心思想,即通過比較哈希來工作,是完全正確的。 (順便說一句,我不是模糊的,NSDictionary不是一個類,但是具有不同實現的許多不同的類從外部看起來都是相同的)。 – Chuck

+0

下降不是正確的轉換 - 你需要以案件摺疊。 – alastair

1

在下面編寫的代碼中,我搜索輸入密鑰的實際密鑰。所以,如果輸入key = @「naMe」,那麼實際的key = @「name」。

NSDictionary *dic=[NSDictionary dictionaryWithObjectsAndKeys:@"John",@"Name",@"123456",@"empId", nil]; 


NSString *[email protected]"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); 
+0

你也可以這樣做。 http://stackoverflow.com/questions/6135000/case-insensitive-search-in-nsmutabledictionary –

+0

這是複雜和緩慢;它首先構造一個包含字典「O(n)」中所有鍵的數組,然後解析NSPredicate並過濾結果數組。 – alastair

12

您需要添加的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; 
} 
+0

這是一個好主意,創建一個類別。但是,爲什麼你需要寫一個for循環。你可以使用隨時可用的使用NSPredicate的函數。 – andyPaul

+0

是的你是對的。 @andyPaul – Siddiq

+8

...你只是將搜索的複雜度從'O(log(N)))'碰撞到'O(N)' – ivanzoid

0

許多答案是正確的,但這裏有一個更例如:

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

我個人覺得這個方法比較容易,但每個人都有他的編程風格。

EDIT

一個不太可讀但較短的溶液:

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 ; 

    }]; 
+0

看看你寫的代碼所佔的行數。但是,是的,你可以將整個模塊寫成一行,但是很難理解。 – andyPaul

+0

有很多行,但代碼量非常小。我不會返回[obj caseInsensitiveCompare:@「Hello」] == NSOrderedSame只是因爲在返回值之前,我必須將* stop設置爲YES,這樣單行就像你說的那樣,很難讀。 –

+0

太棒了,我發現很多編碼風格給我的答案。 – andyPaul

0

如果你只存入,並在一個地方(也許兩個或三個)的,在檢索的NSDictionary,你可以使用

[myString的lowercaseString]

兩個

。如果在您的代碼中使用字典對象,那麼更嚴格的答案會很有用。

+0

這實際上比上面大多數更長的答案要好得多。唯一的錯誤是,在所有情況下,小包裝將無法按預期工作(您確實應該摺疊字符串)。 – alastair

1

正確答案是您應該使用大小寫字母鍵作爲字典鍵。 這與將它們轉換爲大寫或小寫並不相同,它不會破壞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; 
} 

注意區域說法是非常重要的。如果您指定NULL,您將獲得當前的系統區域設置。這在大多數情況下都可以,但土耳其用戶可能會對「我」匹配「我」而不是「ı」感到驚訝。你可能因此想通過[NSLocale currentLocale],如果你保存的結果你可能也想保存區域設置標識符,並從中創建區域設置。

因此,添加到字典時,你現在需要做的

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

,並再次查找

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

最後一個觀察是,你可能希望保存摺疊的情況下,鍵和原始值一起,那麼在每次訪問字典時都不必摺疊它們。