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;
Related
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;
}
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."])
// ...
I have Manager object, and Account object. Manager object has a property Account.
There is a NSArray of Manager objects, which needs to be unique by Account!
example array:
NSArray *managers = #[Manager1(has Account1), Manager2(has Account1), Manager3(has Account1), Manager4(has Acconut2), Manager5(has Account2)];
I need an NSArray with only Manager1 and Manager4.
What you need is distinctUnionOfObjects
[managers valueForKeyPath:#"#distinctUnionOfObjects.Account"];
Here is the documentation
#distinctUnionOfObjects
The #distinctUnionOfObjects operator returns an array containing the distinct objects in the property specified by the key path to the right of the operator.
The following example returns the payee property values for the transactions in transactions with any duplicate values removed:
NSArray *payees = [transactions valueForKeyPath:#"#distinctUnionOfObjects.payee"];
The resulting payees array contains the following strings: Car Loan, General Cable, Animal Hospital, Green Power, Mortgage.
The #unionOfObjects operator is similar, but does not remove duplicate objects.
You will need to override isEqual method inside Manager class (and possibly in Account class as well). After that add Managers to array by first checking, if array already contains Manager with such Account. Check possible solution below:
override Manager isEqual
- (BOOL)isEqual:(id)object {
if (object == self) {
return YES;
} else if (![object isKindOfClass:[Manager class]]) {
return NO;
}
Manager *manager = (Manager *)object;
return [self.account isEqual:manager.account];
}
override Account isEqual (NB! only if it is needed)
- (BOOL)isEqual:(id)object {
if (object == self) {
return YES;
} else if (![object isKindOfClass:[Account class]]) {
return NO;
}
Account *account = (Account *)object;
return [self.someUniqueId isEqual:account.someUniqueId];
}
filter array:
NSMutableArray *filteredManagers = [NSMutableArray array];
[managers enumerateObjectsUsingBlock:^(Manager *manager, NSUInteger idx, BOOL *stop) {
if (![filteredManagers containsObject:manager]) {
[filteredManagers addObject:manager];
}
}];
NSArray *managers = // The 5 Manager objects in your example
NSMutableArray *filteredManagers = [NSMutableArray array];
NSMutableArray *accountsManaged = [NSMutableArray array];
for (Manager *manager in managers) {
if (![accountsManaged containsObject:manager.account]) {
[filteredManagers addObject:manager];
[accountsManaged addObject:manager.account];
}
}
// filteredManagers now contains Manager1 and Manager4
Assuming you have a separate array uniquely (!) containing the Accounts and further assuming your classes are key value compliant, I'd enumerate through that accounts array and find all managers which manage each account. Then pick the first manager and add it to a new array, like so:
NSMutableArray *filteredManagers = [NSMutableArray array];
for (Account *account in accounts) {
NSArray *managersForAccount = [managers filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"account == %#", account];
[filteredManagers addObject:[managersForAccount firstObject]];
}
// do something with filteredManagers
I have array with arrays ex.:
(
(
object,
object
),
(
object,
object,
object,
object
)
)
Each objecthas property .objectID.What is the best way to find object with specific objectID?
Here are two options for you:
Option 1: using nested for loops
CustomObject *searchingObject;
// searching through the first array (which has arrays inside of it)
// Note: this will stop looping if it searched through all the objects or if it found the object it was looking for
for (int i = 0; i < [firstArray count] && searchingObject; i++) {
// accessing the custom objects inside the nested arrays
for (CustomObject *co in firstArray[i]) {
if ([co.objectId == 9235) {
// you found your object
searchingObject = co; // or do whatever you wanted to do.
// kill the inside for-loop the outside one will be killed when it evaluates your 'searchingObject'
break;
}
}
}
Option 2: using blocks:
// you need __block to write to this object inside the block
__block CustomObject *searchingObject;
// enumerating through the first array (containing arrays)
[firstArray enumerateObjectsUsingBlock:^(NSArray *nestedArray, NSUInteger indx, BOOL *firstStop) {
// enumerating through the nested array
[nestedArray enumerateObjectsUsingBlock:^(CustomObject *co, NSUInteger nestedIndx, BOOL *secondStop) {
if ([co.objectId == 28935) {
searchingObject = co; // or do whatever you wanted to do.
// you found your object now kill both the blocks
*firstStop = *secondStop = YES;
}
}];
}];
Although still considered N^2 execution time these will only run as far as they need to. Once they find the object they cease searching.
try it with
[ary filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"objectID == %#", objectID]];
--
id object = nil;
NSPredicate *pred = [NSPredicate predicateWithFormat:#"objectID == %#", objectID];
for(NSArray *subAry in ary)
{
NSArray *result = [subAry filteredArrayUsingPredicate:pred];
if(result && result.count > 0)
{
object = [result objectAtIndex:0];
break;
}
}
for Zombies cares everyone himself :P
If you aren't concerned with order, you could instead use an array of dictionaries where the objectId is the key. That makes your search O(N).
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];
}