In my app I'me getting responses from the server and I have to check that I don't create duplicate objects in the NSArray which contains NSDictionaries. Now to check if the objects exists I do this:
for (int i = 0; i < appDelegate.currentUser.userSiteDetailsArray.count; i++){
NSDictionary *tmpDictionary = [appDelegate.currentUser.userSiteDetailsArray objectAtIndex:i];
if ([[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier]){
needToCheck = NO;
}
if (i == appDelegate.currentUser.userSiteDetailsArray.count - 1 && ![[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier] && needToCheck){
// It means it's the last object we've iterated through and needToCheck is still = YES;
//Doing stuff here
}
}
I set up a BOOL value because this iteration goes numerous times inside a method and I can't use return to stop it. I think there is a better way to perform this check and I would like to hear your suggestions about it.
BOOL needToCheck = YES;
for (int i = 0; i < appDelegate.currentUser.userSiteDetailsArray.count; i++){
NSDictionary *tmpDictionary = [appDelegate.currentUser.userSiteDetailsArray objectAtIndex:i];
if ([[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier]){
needToCheck = NO;
break;
}
}
if (needToCheck) {
//Doing stuff here
}
But, as others have said, you can maybe keep a "summary" in a separate NSSet that you check first, vs spinning through all the dictionaries.
NSDictionary *previousThing = nil;
for (NSDictionary *thing in appDelegate.currentUser.userSiteDetailsArray) {
if ([thing[#"webpropID"] isEqualToString:newWebPropertyIdentifier]) {
previousThing = thing;
break;
}
}
if (previousThing == nil) {
// no previous thing
} else {
// duplicate
}
Related
I am using a nsmutablearray in loop and want to remove its object (or assign nil) that has just traversed.
But if I am doing so, I get an error as <__NSArrayM: 0x8c3d3a0> was mutated while being enumerated.' . The code is as below
- (TreeNode*)depthLimitedSearch:(TreeNode *)current costLimit:(int)currentCostBound {
NSMutableArray *children=[NSMutableArray arrayWithArray:[current expandNodeToChildren]];
for (TreeNode *s in children) {
if (s.puzzleBox.isFinalPuzzleBox) {//checking for final puzzleBox
return s;
}
/*exploredNodes++;
if (exploredNodes %10000==0) {
NSLog(#"explored nodes for this treshold-%d are %d",currentCostBound,exploredNodes);
}*/
int currentCost =[s.cost intValue]+[s.heuristicsCost intValue];
if (currentCost <= currentCostBound) {
//[s.puzzleBox displayPuzzleBox];
TreeNode *solution = [self depthLimitedSearch:s costLimit:currentCostBound];
if (solution!=nil){//&& (bestSolution ==nil|| [solution.cost intValue] < [bestSolution.cost intValue])) {
bestSolution = solution;
return bestSolution;
}
}else {
if (currentCost < newLimit) {
//NSLog(#"new limit %d", currentCost);
newLimit = currentCost;
}
}
// here I want to free memory used by current child in children
[children removeObject:s]
}
children=nil;
return nil;
}
and I have commented the place where I want to release the space used by the child.
You should not be using a for...in loop if you want to remove elements in the array. Instead, you should use a normal for loop and go backwards in order to make sure you don't skip any items.
for (NSInteger i = items.count - 1; i >= 0; i--) {
if (someCondition) {
[items removeObjectAtIndex:i];
}
}
You can collect the items to be removed in another array and remove them in a single pass afterwards:
NSMutableArray *toRemove = [NSMutableArray array];
for (id candidate in items) {
if (something) {
[toRemove addObject:candidate];
}
}
[items removeObjectsInArray:toRemove];
It’s easier than iterating over indexes by hand, which just asking for off-by-one errors.
Not sure how this plays with your early returns, though.
How do I refactor similar methods for the following (Objective C)?
- (void)insertNewSong:(Song *)newSong forArtist:(Artist *)artist {
NSMutableArray *newSongList = [[artist songs] mutableCopy];
BOOL hasInserted = NO;
for (int i = 0; i < [[artist songs] count]; i++) {
Song *existingSong = [[artist songs] objectAtIndex:i];
if ([[newSong title] caseInsensitiveCompare:[existingSong title]] == NSOrderedAscending) {
[newSongList insertObject:newSong atIndex:i];
hasInserted = YES;
break;
}
}
if (hasInserted == NO) {
[newSongList addObject:newSong];
}
artist.songs = newSongList;
}
- (void)insertNewArtistToSongList:(Artist *)newArtist {
BOOL hasInserted = NO;
for (int i = 0; i < [_artists count]; i++) {
Artist *existingArtist = [_artists objectAtIndex:i];
if ([[newArtist name] caseInsensitiveCompare:[existingArtist name]] == NSOrderedAscending) {
[_artists insertObject:newArtist atIndex:i];
hasInserted = YES;
break;
}
}
if (hasInserted == NO) {
[_artists addObject:newArtist];
}
}
For the insertNewSong method, a NSMutableArray [artist songs] containing each Song object is used.
For the insertNewArtist method, a NSMutableArray instance variable _artists containing each Artist Object is used.
Both methods insert an object into an NSMutableArray by comparing the text property of the input object against the text property found within the arrays.
Currently the above methods contain some duplication but is easy to understand (in my case). I was thinking whether there might be a way of simplifying it into a more general method, and does not hurt readability?
There is no general rule, but here are some general rules:
Sometimes it makes sense to combine code like this, sometimes not. Lots of pluses/minuses.
Sometimes it's best to abstract PART of the operation, and leave the other part custom.
Generally, if you have a lot of "if thingA then do this, else that" logic, you've done it wrong (or should not do it at all).
It's best when you can write a single routine and just pass in different parameters (that aren't simply Boolean switches) to differentiate the multiple cases.
It's hard.
And, as a general rule, I don't try too hard to abstract until I have the third instance of nearly the same logic.
(Generally speaking.)
I have done a bit of searching and not really found an answer to my question.
The code below is trying to copy the contents of a NSMutable array that contains some objects.
I have tried the code below however when run there is no error, but the new arrays do not get the objects as I would have thought they would.
csvContent is a array that contains objects from parsing a CSV file and the other NSMutable arrays round1, round2 etc have been defined in the header file.
The NSLOG output is correct but the arrays contain no objects.
for(int loopNumber = 0; loopNumber < [csvContent count]; loopNumber++)
{
if ([[[csvContent objectAtIndex:loopNumber] objectAtIndex:1] isEqualToString:#"1"])
{
[round1 addObject:[csvContent objectAtIndex:loopNumber]];
NSLog(#"round 1");
}
if ([[[csvContent objectAtIndex:loopNumber] objectAtIndex:1] isEqualToString:#"2"])
{
[round2 addObject:[csvContent objectAtIndex:loopNumber]];
NSLog(#"round 2");
}
if ([[[csvContent objectAtIndex:loopNumber] objectAtIndex:1] isEqualToString:#"3"])
{
[round3 addObject:[csvContent objectAtIndex:loopNumber]];
NSLog(#"round 3");
}
}
Did you actually create and initialize the arrays for round1, round2, and round3? In other words, make sure they are not nil when this loop is run.
Also, your code is terribly inefficient. You call [csvContent objectAtIndex:loopNumber] six times inside the loop. Try this (I'm using i instead of loopNumber to save typing):
for (int i = 0; i < csvContent.count; i++) {
NSArray *loopContent = csvContent[i];
NSString *val = loopContent[1];
if ([val isEqualToString:#"1"]) {
[round1 addObject:loopObject];
} else if ([val isEqualToString:#"2"]) {
[round2 addObject:loopObject];
} else if ([val isEqualToString:#"3"]) {
[round3 addObject:loopObject];
}
}
What is faster in objective C and iphone? self enumeration or for loop?
i have 2 fragments of code to help me compare.
for this example we have as a fact that array is an NSMutableArray with "x" items.
Case 1:
-(void)findItem:(Item*)item
{
Item *temp;
for (int i = 0 ;i<[array count];i++)
{
temp = [array objectAtIndex:i];
if(item.tag == temp.tag)
return;
}
}
Case 2:
-(void)findItem:(Item*)item
{
for(Item *temp in array)
{
if(item.tag == temp.tag)
return;
}
}
it is almost obvious that case2 is faster, is it?
It's called fast enumeration, for a reason.
See: http://cocoawithlove.com/2008/05/fast-enumeration-clarifications.html
i have some code that requires the use of a for loop to read variables from an array.
int size=sizeof names;
NSLog(#"thelast one is %d",size);
NSString *usersName=userName.text;
NSString *usersPass=passWord.text;
for (i=0; i<=size;i++){
NSString *namesArray=[names objectAtIndex:i];
NSString *passArray=[pass objectAtIndex:i];
NSLog(#"namesArray %#",namesArray);
NSLog(#"passArray %#",passArray);
if([namesArray isEqualToString:usersName]){
userValid=1;
NSLog(#"The content of arry4 is %#",namesArray);
}
if([passArray isEqualToString:usersPass]){
passValid=1;
NSLog(#"The content of arry4 is %#",passArray);
}
else {
userValid=0;
passValid=0;
}
}
I've been having some problems because every time this function is called from within the program, it's almost as if the 'sizeof names' is wrong, therefore not all values in the array are checked.
I'm generally a Java programmer so i'm used to names.length, and i was told sizeof names is essentially the same thing... any help?
Cheers.
Don't use sizeof. Use [names count].
You want to use [names count] not sizeof names. Sizeof is going to give you the size of the actual names object pointer itself and not the number of elements, since it's dynamic memory type.
To get the number of elements stored in an NSAarray you should use the instance method count, which returns an NSUInteger.
Alternatevely, you can iterate over these elements using the for in loop, which is available also in Java, if I recall correctly.
for (MyClass *element in myArray) {
NSLog(#"%#", element);
}
Note that sizeof is a C operator that returns the size in bytes of its operand, so it doesn't tell you how many elements are stored in that NSArray, but the size in bytes of one NSArray instance.
I know your question has already been answered - but here is a more Cocoa way of writing it
NSString *userName = userName.text;
NSString *userPass = passWord.text;
// Use a block enumerator
NSUInteger nameIdx = [names indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return ([obj isEqualToString:userName]);
}];
// Is the name in the array
if (nameIdx == NSNotFound) {
// Name not in array - so set to zero
userValid = 0;
passValid = 0;
} else {
userValid = 1;
// See if the corresponding password is correct
NSString password = [pass objectAtIndex:nameIdx];
if (![password isEqualToString:userPass]) {
passValid = 0;
} else {
passValid = 1;
}
One can also use Fast Enumeration, in some cases it can be more clear to a reader:
NSString *userName = userName.text;
NSString *userPass = passWord.text;
BOOL userValid = NO;
BOOL passValid = NO;
int index = 0;
for (NSString *eachName in namesArray) {
if ([eachName isEqualToString:userName) {
userValid = YES:
if ([[passArray objextAtIndex:index] isEqualToString:passWord) {
passValid = YES;
}
break;
}
index += 1;
}