14

Ok a pretty simple question.. in c++ it seems to work but in objective-c i seem to struggle with it :S .. If you want to compare two arrays it should be something like this right

for ( int i = 0; i < [appdelegate.nicearray count]; i++ ) 
{ 
  if ( appdelegate.nicearray[i] == appdelegate.exercarray[i] )
  { 
     NSLog(@"the same elements in this selection");
  }
}

what's the problem exactly ?

hakre
  • 193,403
  • 52
  • 435
  • 836

5 Answers5

44

These are Cocoa array objects (instances of NSArray), not C arrays or C++ vectors, and remember that Objective-C does not have operator overloading. The only things you can do with an object are pass it around, store it in variables, and send messages to it.

So the array-subscript operator is wrong with Objective-C objects. I don't think it's even linguistically valid to dereference a pointer to an Objective-C object, so this code should be giving you a compiler error. I may be misremembering, though. If it does make it to runtime, that code will crash sooner or later, since you're accessing memory beyond the ends of the array objects.

(EDIT from the year 2013: Objective-C now supports subscripting of objects. This ultimately translates into the appropriate objectAtIndex: or replaceObjectAtIndex:withObject: message. So, the code in the question would actually work now, although it's still not the proper way to simply walk an array, much less to compare two arrays.)

The proper way to retrieve an object from an NSArray object by its index is not to use the array-subscript operator, but to send the array object the objectAtIndex: message:

[myArray objectAtIndex:i]

The proper way to iterate on the elements of an array object, assuming you don't really need the index for something else (such as replacing objects in a mutable array), is to loop on it directly (this is called “fast enumeration”):

for (MyObject *myObject in myArray) {
    …
}

NSArray also responds to objectEnumerator and reverseObjectEnumerator, which return a similarly-iterable object. Of the two, reverseObjectEnumerator is the more useful in new code, since you can just iterate on the array directly to iterate forward. Both of them were most useful before fast enumeration existed; that code looked like this:

NSEnumerator *myArrayEnum = [myArray objectEnumerator];
MyObject *myObject;
while ((myObject = [myArrayEnum nextObject])) {
    …
}

(Yes, that's an assignment in the condition. Deliberately, hence the extra (). We coded boldly back then, didn't we?)

For what you're doing, though, you more likely want to send one of the arrays an isEqualToArray: message, as Williham Totland suggested:

BOOL theyAreEqual = [myFirstArray isEqualToArray:mySecondArray];

This will make sure both arrays have the same length, then walk them both in lock-step, sending isEqual: to each pair of objects. It'll return YES if every isEqual: message returned YES; NO otherwise. The arrays may contain different objects, but as long as each pair is equal, the arrays themselves are equal.

That assumes you want object equality. Two separate objects are equal if one of them responds with YES when you send it an isEqual: message and pass the other object. If you meant to compare the identities of the objects, then you do need to do the lock-step loop yourself and use ==:

BOOL arraysContainTheSameObjects = YES;
NSEnumerator *otherEnum = [otherArray objectEnumerator];
for (MyObject *myObject in myArray) {
    if (myObject != [otherEnum nextObject]) {
        //We have found a pair of two different objects.
        arraysContainTheSameObjects = NO;
        break;
    }
}

But that's unlikely. Most of the time, I have wanted to test the objects' equality, not identities, so isEqualToArray: is what I wanted.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • Random follow up question: is `[nsArrrayInstance isEqual:otherInstance]` equivalent to `[nsArrrayInstance isEqualToArray:otherInstance]`? – nmr Jun 25 '12 at 17:10
  • 1
    @nmr: Almost. There are two edge-case differences: http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/FrameworkImpl.html#//apple_ref/doc/uid/20001286-1007347 http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW61 – Peter Hosey Jun 26 '12 at 03:04
  • So to boil that down, the first link states that `isEqualTo...` may assume the argument to be of the correct type~ I'm not sure what edge case the second link is discussing. Perhaps I could trouble you to elaborate on that? – nmr Jun 27 '12 at 21:59
  • 2
    @nmr: “In all `isEqualTo:` methods of the Cocoa frameworks, `nil` is not a valid parameter and implementations of these methods may raise an exception upon receiving a `nil`. However, for backward compatibility, `isEqual:` methods of the Cocoa frameworks do accept `nil`, returning `NO`.” – Peter Hosey Jun 27 '12 at 22:53
  • @Peter Hosey Cool, thanks. So would you say that a corollary of all this is that if you implement `isEqualTo:` it would be weird to not also implement `isEqual:`? And that given arguments of type `` they should return the same value? – nmr Jun 28 '12 at 02:46
  • @PeterHosey : I do realise that this is an old thread, but maybe you could update your answer as subscripts operator are now present in Objective-C. – Losiowaty Oct 07 '16 at 08:46
26

You want the isEqualToArray: method. As in:

if ([arrayOne isEqualToArray:arrayTwo]) {
  // Do something
}

This will recursively compare the two arrays, while having the advantage of not being needlessly circuitous and not requiring a loop.

Williham Totland
  • 28,471
  • 6
  • 52
  • 68
1

Try telling us the result you're getting when you run this code. The approach is correct, but try this one:

for (int i =0; i< appdelegate.nicearray.count; i++)
{
    if ([[appdelegate objectAtIndex:i] isEqual: [appdelegate.exercarray objectAtIndex:i]])
    {
        NSLog(@"the same");
    }
}  
Dan Lorenc
  • 5,376
  • 1
  • 23
  • 34
  • You should generally compare objects with `isEqual:`, not pointer equality. – Chuck Jul 16 '09 at 14:13
  • Moreover, you could just use `isEqualToArray:` to do this, as Williham Totland suggested. (Props for pointing out that he should be using `objectAtIndex:` instead of the array-subscript operator, though.) – Peter Hosey Jul 16 '09 at 14:58
1

Here's a little one I put together based on the top ranked example. This merely checks that the arrays contains the same values, irrespective of order and if there are any duplicates. I mainly use this to compare keys of two dictionaries (which often return their allKeys arrays in various sort orders) to see if they contain the same objects. Thanks Peter Hosley for providing the example I adapted this from.

#pragma mark - Arrays
// Check to see if arrays contain the same elements, not necessarily in the same order
// This is different from [array isEqualToArray:responseKeys] which demands the same order in both arrays
// ## Does not compensate for duplicate entries in an array
+ (BOOL)doArraysContainTheSameObjects:(NSArray *)firstArray withArray:(NSArray *)secondArray {
    BOOL arraysContainTheSameObjects = YES;

    for (id myObject in firstArray) {
        if (![secondArray containsObject:myObject]) {
            // We have found an object that is not in the other array.
            arraysContainTheSameObjects = NO;
            break;
        }
    }
    return arraysContainTheSameObjects;
}
Alex Zavatone
  • 4,106
  • 36
  • 54
  • To be honest, we should also check to see if the first array contains all the objects in the second array if this is to become general purpose. – Alex Zavatone Jul 28 '15 at 18:03
  • Also if the firstArray is nil and second array is a blank array then the above method will return true. – IronMan Feb 23 '18 at 12:39
0

I do the following when comparing arrays:

  • Check to see if any of the arrays are nil when the other is not
  • Check to see if the lengths are the same
  • Iterate (using a for loop like you have) over each element checking the matching element in the other array.

To compare elements you need to define what you want to regard as being "equal". Are they equal only if the pointers in the array are equal or can they be equal if the content is equal too.

For the pointer case, you can use ==.

For the deep comparison you might need to use CompareTo or something similar.

rein
  • 32,967
  • 23
  • 82
  • 106