Obj C - NSMutableArray containsObject return true - ios

NSMutableArray containsObject returns true even the address and data is different.
I've seen this post NSMutableArray containsObject returns true, but it shouldnt
already but still I'm not finding my solution:
Below is my scenario:
NSMutableArray *destClasses = [NSMutableArray array];
id sourceClasses = [dict objectForKey:#"Classes"];
if ([sourceClasses isKindOfClass:[NSArray class]]) {
for (NSDictionary *class in sourceClasses) {
MyClass *a = [[MyClass alloc] init];
[a arrangeClassWithDictionary:classDict]; //this methods assigns value to a from classDict
if (![destClasses containsObject:a]) {
[destClasses addObject:a];
}
}
}
In the first iteration destClasses adds an MyClass object and on the second iteration [destClasses containsObject:a] returns true even though the a has different address and different values assigned.
What I'm doing wrong here. Please help.

I got the answer.
containsObject: which sends the isEqual: message to every object it
contains with your object as the argument. It does not use == unless
the implementation of isEqual: relies on ==.
I've to override the isEqual: method to provide equality checking for my object fields like below,
- (BOOL)isEqual:(id)object
{
BOOL result = NO;
if ([class isKindOfClass:[self class]]) {
MyClass *otherObject = object;
result = [self.name isEqualToString:[otherObject name]];
}
return result;
}

Related

How to check If array has same object value before adding new value

I am getting data from Dictionary. It works well and stores data in NSMutableArray I want that before adding object into need to make sure that Array does not contain same object with Same Name and Type. Please see below.
Before inserting object we should check that it does not contain object with Type and Name if contains no need to insert.
NSArray *resultDic = [result1 objectForKey:#"results"];
for (int i = 0; i<[resultDic count]; i++) {
id item = [resultDic objectAtIndex:i];
NSDictionary *jsonDict = (NSDictionary *) item;
GetData *theObject =[[GetData alloc] init];
NSString*error = [jsonDict valueForKey:#"error"];
if(![error isEqualToString:#"No Record Found."])
{
[theObject setVaccineID:[jsonDict valueForKey:#"ID"]];
[theObject setVaccineName:[jsonDict valueForKey:#"Name"]];
[theObject setVaccinationType:[jsonDict valueForKey:#"Type"]];
[theObject setVaccineType:[jsonDict valueForKey:#"VType"]];
[theObject setFarmName:[jsonDict valueForKey:#"FName"]];
[theObject setDay:[jsonDict valueForKey:#"Day"]];
[theObject setAddedDateTime:[jsonDict valueForKey:#"DateTime"]];
[appDelegate.dataArray addObject:theObject];
}
}
A general purpose solution is to teach your GetData object how to compare itself to others. If they can be compared, then it will be easy to determine if a match is in any collection (and you might want to compare them in other contexts, too). Do this by implementing isEqual:. That might look something like this:
// in GetData.m
- (BOOL)isEqual:(id)object {
if ([object isKindOfClass:[GetData self]]) {
// assuming that the object is fully characterized by it's ID
return [self.vaccineId isEqual:((GetData *)object).vaccineId];
}
else {
return NO;
}
}
// have the hash value operate on the same characteristics as isEqual
- (NSUInteger)hash {
return [self.vaccineId hash];
}
With that done, you can take advantage of NSArray's containsObject:.
// ...
if(![appDelegate.dataArray containsObject:theObject] && ![error isEqualToString:#"No Record Found."])
// ...

Returning an NSMutableArray in objective C

I have been trying to work out how to return an MSMutableArray in objective c, I have this code here.
- (NSMutableArray*)generateRandomNumber{
NSMutableArray *unqArray=[[NSMutableArray alloc]init];
int randNum;
int counter = 0;
while (counter< 6) {
randNum = arc4random_uniform(40.0);
if (![unqArray containsObject:[NSNumber numberWithInt:randNum]]) {
[unqArray addObject:[NSNumber numberWithInt:randNum]];
counter++;
}
}
return unqArray;
}
- (IBAction)button:(id)sender {
NSMutableArray *results = [[NSMutableArray alloc]init];
*results = generateRandomNumber();
}
This is my code at the moment, where it says,
NSMutableArray *results = [[NSMutableArray alloc]init];
I get the following errors...
Implicit declaration of function 'generateRandomNumber' is invalid in C99
Assigning to 'NSMutableArray' from incompatible type 'int'
If anybody is willing to show me my mistake and help me out as well as many others I will appreciate it as much as possible.
Thanks to all who help me out!!!
generateRandomNumber is a method, not a C function, so use [self generateRandomNumber] to call it, and you are assigning results incorrectly, so:
results = [self generateRandomNumber];
Alternatively if you want to define it as a C function, use:
NSMutableArray *generateRandomNumber() {
...
}
Also, as pointed-out by #Larme, there is no need to allocate results before assigning it from generateRandomNumber.
- (IBAction)button:(id)sender {
NSMutableArray *results = [[NSMutableArray alloc]init];
results = [self generateRandomNumber];
}
Or more simply:
- (IBAction)button:(id)sender {
NSMutableArray *results = [self generateRandomNumber];
// Do something with this Array
}
Though you should consider a different name for that method as currently it sounds like it is returning one number, not an array of random numbers.
When calling/using a function in Objective-C
it's [self generateRandomNumber]
When you declare a variables it's NSMutableArray *results = [[NSMutableArray alloc]init]; and use if like results = mutableArry, * before it is not needed..
about your problem, you better do it like this:
- (IBAction)button:(id)sender
{
NSMutableArray *results = [self generateRandomNumber];
}
you dont need to allocate anymore, because you are passing an mutable array that is already allocated..
Your method generateRandomNumbers should work as expect the problem is here:
- (IBAction)button:(id)sender {
NSMutableArray *results = [[NSMutableArray alloc]init];
*results = generateRandomNumber();
}
The first thing is that you are creating an unneeded mutable array, second your are trying to substitute the value of the pointer that points to result array, third generateRandonNumber is a method not a function you should call it like [self generateRandomNumber].
Also I would implement a optimization since I'm pretty sure that you are not going to modify the random number array, the returned instance should be an immutable copy.
Here the final code:
- (NSArray*)generateRandomNumber{
NSMutableArray *unqArray=[[NSMutableArray alloc]init];
int randNum;
int counter = 0;
while (counter< 6) {
randNum = arc4random_uniform(40.0);
if (![unqArray containsObject:[NSNumber numberWithInt:randNum]]) {
[unqArray addObject:[NSNumber numberWithInt:randNum]];
counter++;
}
}
return unqArray.copy;
}
- (IBAction)button:(id)sender {
NSArray *results = nil;
results = [self generateRandomNumber];
}
Your generateRandomNumbers method should work with no problem, but in the IBAction there you try to put an INT directly into the array, you need to wrap it in an NSNumber like you have in the other method there
Your method is correct.
What you are doing wrong is here: *results = generateRandomNumber();
There is no need of an * here,because in this way you are trying to assign the pointer address of your array to results object.
Secondly you are trying to call an Objective-C method in C syntax.
So the correct syntax would be: results = [self generateRandomNumber];

Check if unique object exists in NSArray

I have an NSMutableArray where objects can be added to. The objects added are SUPDataValue objects containing a bunch of SUP data.
In an other view, I take this object array and divide it into an NSMutableArray containing an NSMutableArray for every section in the tableview.
When add another SUPDataValue object to my initial Object array, and I switch back to my table view, I want to re-read all the object from the object array and see if those objects exist in my layered array.
I am using the "objectExists" and it works great... however... if I add the same object twice to my object array, it will always assume it exists in a layer.
This is my code:
- (void)setInitialComponents:(NSMutableArray*)components
{
if (self.componentLayer)
{
for (SUPDataValueList *val in components)
{
BOOL found = NO;
for (NSMutableArray *layer in self.componentLayer)
{
if ([layer containsObject:val])
{
found = YES;
}
}
if (!found)
{
[[self.componentLayer objectAtIndex:0] addObject:val];
}
}
}
else {
self.componentLayer = [NSMutableArray array];
// Add the no-layer layer (section 0)
[self.componentLayer addObject:[NSMutableArray array]];
if (self.addMode)
{
[[self.componentLayer objectAtIndex:0] addObjectsFromArray:components];
}
else {
for (SUPDataValueList * val in components)
{
int layer = [[NSString stringWithFormat:#"%#", [val item:38]] intValue];
if (self.componentLayer.count < layer)
{
[self.componentLayer insertObject:[NSMutableArray array] atIndex:layer-1];
}
[[self.componentLayer objectAtIndex:layer-1] addObject:val];
}
}
}
[self.tableView reloadData];
}
As you might have guessed, my problem is here:
if ([layer containsObject:val])
{
found = YES;
}
I would like to check if an unique object exist in that array (using memory allocation ID or something?)
How do I do that?
The containsObject method will invoke isEqual: on the underlying objects being compared.
Unless you implement isEqual: in the SUPDataValueList object, it will simply do a pointer comparison which is the default behavior of isEqual in NSObject.
You're looking for -[NSArray indexOfObjectIdenticalTo:], which uses the objects' addresses to determine a match.
found = [layer indexOfObjectIdenticalTo:val] != NSNotFound;

How to add unique object to nsmutablearray?

I have an NSObject with NSStrings inside. How do I add objects with unique obj.name only to an NSMutableArray? I tried NSOrderedSet but it only works if you add NSString to an array and not objects with NSString inside.
Example.
##interface MyObject : NSObject
#property (strong, nonatomic) NSString *name;
#end
NSMutableArray *array = {MyObject.name,MyObject.name,MyObject.name};
How do I make sure that no two MyObjects have the same name?
Use NSPredicate for seraching object in NSMutableArray if not present then add it to NSMutableArray.
Try this.
NSArray * filtered = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"name = %#", #"MyObject.name"]];
if(![array count])
[array addObject:MyObject ];
All NSSet classes use isEqual: in combination with hash: to compare equality.
Because you have not redefined these simply storing two objects with the same name in a set will be possible as the NSObject implementation of isEqual: and hash: will be used.
The documentation of NSObject Protocol talks about overriding isEqual and hash.
This previous answer on Stackoverflow details how to implement hash and isEqual correctly.
In your own implementation of hash you can use NSString's hash method.
Example
- (NSUInteger) hash {
NSUInteger prime = 31;
NSUInteger result = 1;
result = prime * result + [super hash];
result = prime * result + self.name == nil ? 0 : [self.name hash];
return result;
}
- (bool) isEqual:(id)other {
if (other == self) {
return YES;
}
if (!other || ![other isKindOfClass:[self class]]) {
return NO;
}
return [self.name isEqualToString:other.name];
}
Personally, I would use a NSMutableDictionary with MyObject.name as the key. That way all you would have to do is this:
if( myDictionary[MyObject.name] == nil )
{
myDictionary[MyObject.name] = MyObject;
}
Much more efficient than using a regular NSMutableArray based on the number of additions you are doing. Plus if you want to get access to an array of all the values, all you have to do is:
NSArray *array = [myDictionary allValues];
The Big-O Runtime for NSPredicate is O(n) where the dictionary method is O(1).

NSMutableSet removeObject can not remove object

I am trying to create a set of objects using NSMutableSet. The object is a Song, each tag has a name and an author.
code:
#import "Song.h"
#implementation Song
#synthesize name,author;
-(Song *)initWithName:(NSString *)n andAuth:(NSString *)a {
self = [super init];
if (self) {
name = n;
author = a;
}
return self;
}
-(void)print {
NSLog(#"song:%#; author:%#;", name,author);
}
-(BOOL)isEqual:(id)obj {
//NSLog(#"..isEqual");
if([[obj name] isEqualToString:name]
&& [[obj author] isEqualToString:author]) {
return YES;
}
return NO;
}
-(BOOL)isEqualTo:(id)obj {
NSLog(#"..isEqualTo");
if([[obj name] isEqualToString:name]
&& [[obj author] isEqualToString:author]) {
return YES;
}
return NO;
}
#end
Then put this object into NSMutableSet:
int main(int argv, char *argc[]) {
#autoreleasepool {
Song *song1 = [[Song alloc] initWithName:#"music1" andAuth:#"a1"];
Song *song2 = [[Song alloc] initWithName:#"music2" andAuth:#"a2"];
Song *song3 = [[Song alloc] initWithName:#"music3" andAuth:#"a3"];
Song *needToRemove = [[Song alloc] initWithName:#"music3" andAuth:#"a3"];
NSMutableSet *ns = [NSMutableSet setWithObjects:song1, song2, song3, nil];
[ns removeObject:needToRemove];
for (Song *so in ns) {
[so print];
}
}
}
But the strange thing happend,music3 is still in the NSMutableSet。But change to NSMutableArray,the music3 can delete.NSMutableArray's removeObject call object's isEqual method. I find the explain of the removeObject.Just a sentence:
Removes a given object from the set.
It's not explain how it works.How to delete object like this way?NSMutableSet's removeObject call which method?
The objective-c collection classes rely on - (NSUInteger)hash to figure out equal objects.
If your objects returns YES for isEqual: but a different hash, classes like NSSet will consider the objects different.
See the discussion of hash:
If two objects are equal (as determined by the isEqual: method), they must have the same hash value. This last point is particularly important if you define hash in a subclass and intend to put instances of that subclass into a collection.
Implement the hash method. Something like this should work:
- (NSUInteger)hash {
return [self.author hash] ^ [self.name hash];
}

Resources