13

I'm loading a dictionary (list of word, not the class) into a NSSet as NSStrings. I then repeatedly send this set the message -containsObject:someNSString. But it always returns false. I wrote some code to test it:

NSLog(@"Random from dictionary: %@", [dictionary anyObject]);
NSString *test = [NSString stringWithFormat:@"BEMIRED"];
NSLog(@"To match this word: %@", test);
if ([dictionary containsObject:test])
    NSLog(@"YES!");

In the log I get the following:

Random from dictionary: BEMIRED
To match this word: BEMIRED

(I'm missing the "YES!")

When I try using CFShow(dictionary) I can see that it actually contains Strings and that everything. An example:

0 : <CFString 0xc3bd810 [0x1386400]>{contents = "BEMIRED"}
3 : <CFString 0xdf96ef0 [0x1386400]>{contents = "SUBJECTIFIED"}

Can anyone please help me here? Thanks!

Mike Weller
  • 45,401
  • 15
  • 131
  • 151
RikardA
  • 181
  • 1
  • 9
  • BEMIRED was supposed to be bolded. It doesnt actually show the ** before and after in the log. Sorry, my first time posting here. – RikardA Aug 12 '11 at 08:30
  • 1
    Can you show us how `dictionary` is created? – jtbandes Aug 12 '11 at 08:32
  • Maybe a stupid suggestion, but have you tried `[[dictionary allValues] containsObject:...]`? – Fabian Schuiki Aug 12 '11 at 08:33
  • I'm using Dave DeLongs DDFileReader: http://stackoverflow.com/questions/3707427/how-to-read-data-from-nsfilehandle-line-by-line#3711079 – RikardA Aug 12 '11 at 08:34
  • @Fabian I haven't but it's cruicial that i don't have to use an array when searching since that severly slows down execution. Maybe you meant it as a test? I'll try it. – RikardA Aug 12 '11 at 08:36
  • I made some changes to how i load the dictionary, now it's working. Sorry for bothering you with irrelevant question. – RikardA Aug 12 '11 at 08:54
  • 1
    It’s not an irrelevant question, are you able to to explain the problem in an answer? It could be interesting for us. – zoul Aug 12 '11 at 08:56
  • @RikardA: please write an answer that explains how you fixed the problem. – JeremyP Aug 12 '11 at 09:17
  • And please post your solution as an answer (you may even accept it). – DarkDust Aug 12 '11 at 09:23
  • @JeremyP and DarkDust: I will as soon as I'm allowed to but since I'm a new member it will be another 6 hours. :) – RikardA Aug 12 '11 at 09:39
  • This is a preview:Ok so I solved the problem and it had nothing to do with the containsObject method. As I commented i used Dave DeLongs DDFileReader. So by using CFShow on the entire dictionary I noticed that every word had a new line at the end of it. So instead of the -readLine method i used the -readTrimmedLine (bot methods in above mentioned file reader). This solved the problem for me. For future forum visitors I'd like to draw attention to the discussion DarkDust and zoul had about -containsObject and -member (both methods of NSSet) which it turns out both uses the -isEqual method. – RikardA Aug 12 '11 at 09:40

3 Answers3

28

NSSet uses isEqual: to test for object equality, which NSString overrides to perform a string comparison as you would expect. The follow unit test passes:

- (void)testSetStrings
{
    NSSet *set = [NSSet setWithObject:@"String 1"];

    // I've used the UTF8 initializer to avoid any cleverness from reusing objects
    NSString *string1 = [[[NSString alloc] initWithUTF8String:"String 1"] autorelease];

    // Test the references/pointers are not the same
    STAssertTrue([set anyObject] != string1, nil);

    STAssertTrue([set containsObject:string1], nil);
}

We can see the two strings have different pointer values, but the set still returns YES for the containsObject: call.

So I would guess your strings are not in fact equal. I would check for hidden whitespace or other similar issues.

Mike Weller
  • 45,401
  • 15
  • 131
  • 151
0

The -[NSSet containsObject:] seems to check for the pointer value only (the documentation is very lacking for that method), not for object equality. So you need to use -[NSSet member:] instead, which uses isEqual: to check whether an object that is considered to be equal is in your set.

if ([dictionary member:test])
    NSLog(@"YES!");

Edit: Actually it seems that containsObject: does use isEqual: as well. They only seem to differ in what they return (containsObject: returns a BOOL while member: returns id). I'm letting this answer stay for documentation purposes.

DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • 1
    This is crazy, have you some pointers to explain the behaviour? Did you test it? According to [this Cocoa Builder thread](http://www.cocoabuilder.com/archive/cocoa/298982-nsset-containsobject-returns-no-when-it-should-return-yes.html) `containsObject:` should use object equality just as expected. – zoul Aug 12 '11 at 08:43
  • It doesn't seem to work there either. I'm starting to think it's a problem with the way i'm reading the file. – RikardA Aug 12 '11 at 08:45
  • @zoul: You're right. Given the fact that nothing specific is mentioned in the documentation for `containsObject:`, but `member:` specifically says it's using `isEqual:`, I was assuming that `containsObject:` would simply check for the pointer value which would be pretty fast. I searched for a confirmation, but other than the thread you're quoting (which says I was wrong) I couldn't find anything. – DarkDust Aug 12 '11 at 09:19
0

Ok so I solved the problem and it had nothing to do with the containsObject method. As I commented i used Dave DeLongs DDFileReader found here: Dave DeLongs DDFileReader

So by using CFShow on the entire dictionary I noticed that every word had a new line at the end of it. So instead of the -readLine method i used the -readTrimmedLine (bot methods in above mentioned file reader). This solved the problem for me.

For future forum visitors I'd like to draw attention to the discussion DarkDust and zoul had about -containsObject and -member (both methods of NSSet) which it turns out both uses the -isEqual method.

Community
  • 1
  • 1
RikardA
  • 181
  • 1
  • 9