copy and autorelease in enumeration - objective-c

I am trying to change the contents of an array while enumeration. To avoid the warning ,"the array was mutated while being enumerated" i made a copy of the array and done the autorelease like this-
int iKeyArrayCount=0;
for(NSString *keyEntity in [[keyArray copy]autorelease])
{
[keyArray replaceObjectAtIndex:iKeyArrayCount withObject:[keyEntity stringByReplacingOccurrencesOfString:#"\"" withString:kMPVTBlankString]];
[keyArray replaceObjectAtIndex:iKeyArrayCount withObject:[keyEntity stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
iKeyArrayCount++;
}
My confusion is regarding the enumeration over that copy of keyArray. While enumeration is it like , a copy of keyArray is formed each time the for loop executes? Or only one copy of that keyArray is formed during the whole process of enumeration.

In that code snippet [[keyArray copy] autorelease] is executed once only, and the result is used at the object being iterated.
In other words, "only one copy of that keyArray is formed during the whole process of enumeration" is correct.

It doesn't make sense to copy the whole array for this. You could also just use a regular for-loop instead of the enumeration:
for (NSInteger i = 0; i < [keyArray count]; i++)
{
NSString *keyEntity = [keyArray objectAtIndex:i];
NSString *newKeyEntity = [keyEntity stringByReplacingOccurrencesOfString:#"\\" withString:#" "];
newKeyEntity = [newKeyEntity stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[keyArray replaceObjectAtIndex:i withObject:newKeyEntity];
}
Things may get dirty if you start adding/deleting objects in the loop, but as long as you just replace them this is just fine.
As you may have already noticed that stringByReplacingOccurrencesOfString:withString: is useless in your example, since you replace it in the next line once again.

Related

removing null from arrays in Object-c

I have this snipped of code that results in an array with a whole bunch of "<null>" throughout and I need to figure out how to remove them. Obviously after smashing my head against the keyboard I'm asking for some help.
In my .h I have declared:
NSArray *sortedContacts;
NSArray *rawContacts;
And then in .m:
-(void) buildContacts {
ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
NSArray *contacts = [addressBook people];
rawContacts=contacts;
NSArray *firstNames = [rawContacts valueForKey:#"First"];
NSArray *lastNames = [rawContacts valueForKey:#"Last"];
NSArray *organization = [rawContacts valueForKey:#"Organization"];
NSMutableArray *fullNames = [NSMutableArray array];
for(int i = 0; i < [firstNames count]; i++)
{
NSString *fullName = [NSString stringWithFormat:#"%# %# %#",
[firstNames objectAtIndex:i],
[lastNames objectAtIndex:i],
[organization objectAtIndex:i]];
[fullNames addObject:fullName];
}
NSMutableArray *fullList = [[NSMutableArray alloc]initWithArray:fullNames];
[fullList removeObjectIdenticalTo: #"<null>"];
sortedContacts = [fullList sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"%#",sortedContacts);
}
I've tried so many things that I just can't see the forest for the trees anymore.
The text <null> is how the singleton instance of NSNull describes itself. That is, it's what -[NSNull description] returns.
In turn, these NSNull objects are getting into your firstNames, lastNames, and organization arrays because that's what Key-Value Coding does when you call -valueForKey: on an array and some of the elements return nil when that message is forwarded on to them with the same key. That is, calling [rawContacts valueForKey:#"First"] causes NSArray to call [element valueForKey:#"First"] for each element in rawContacts and to put the result in the array it builds. But, since an array can't contain nil, if one of those elements returns nil from [element valueForKey:#"First"], an NSNull object is added in its place.
Then, you are formatting the string fullName from the corresponding elements of firstNames, lastNames, and organization. You need to check if any of those elements are NSNull using if ([value isKindOfClass:[NSNull class]]) and handling that. For instance, you might just skip that record. Or you might combine the available fields and leave out any unavailable ones.
In any case, none of the elements of fullList will be #"<null>" because formatting values into #"%# %# %#" can never result in that string. (It might be #"<null> <null> <null>" or something like that, but never just #"<null>".)
A quick look at your code suggests you cannot get any empty strings added to your array, (a) you add elements using:
[fullNames addObject:fullName];
and fullName is created using:
[NSString stringWithFormat:#"%# %# %#" ...
so even if the %#'s get replaced by nothing you'll still have 2 spaces...
Maybe this is why all the things you've tried fail, if you're looking for empty strings you won't find them.
(Addendum: Question now says you're looking for #"<null>", you won't get that either for the same reason - there is at least two spaces in your string.)
The simple answer to removing invalid entries in fullNames is not to add them in the first place. You are adding elements in a loop (for), and conditional logic (e.g. if) inside the loop to determine whether you have something valid to add - however you define "something valid" - and only add an item to fullNames if so.
HTH
I'm not really familiar with the AddressBook framework, however this might be what's causing the confusion:
The values you collect in your arrays firstNames, lastNames and organization can be of type NSString or NSNull. You have to do any null-checking within the for-loop, before the fullName-string is constructed.
Remove this useless line:
[fullList removeObjectIdenticalTo: #"<null>"];
And replace the contents of your for-loop with the following code:
for(int i = 0; i < [firstNames count]; i++)
{
NSString *firstName = [firstNames objectAtIndex:i];
NSString *lastName = [lastNames objectAtIndex:i];
NSString *org = [organization objectAtIndex:i];
NSMutableArray *namesArray = [NSMutableArray array];
if ([firstName isKindOfClass:[NSString class]])
[namesArray addObject:firstName];
if ([lastName isKindOfClass:[NSString class]])
[namesArray addObject:lastName];
if ([org isKindOfClass:[NSString class]])
[namesArray addObject:org];
if (namesArray.count > 0)
[fullNames addObject:[namesArray componentsJoinedByString:#" "]];
}

Having trouble taking an index of an array and making it an NSString

I get an array from a JSON and I parse it into an NSMutableArray (this part is correct and working). I now want to take that array and print the first object to a Label. Here is my code:
NSDictionary *title = [[dictionary objectForKey:#"title"] objectAtIndex:2];
arrayLabel = [title objectForKey:#"label"];
NSLog(#"arrayLabel = %#", arrayLabel); // Returns correct
//Here is where I need help
string = [arrayLabel objectAtIndex:1]; //I do not get the first label (App crashes)
NSLog(#"string = %#", string);
other things that I have already tried are as follows:
string = [NSString stringWithFormat:#"%#", [arrayImage objectAtIndex:1]];
and
string = [[NSString alloc] initWithFormat:#"%#", [arrayImage objectAtIndex:1]];
Any help is greatly appriciated!
EDIT: The app does not return a single value and crashes.
Your code doesn't match the structure of your JSON. In your comment on the deleted answer, you said you got an exception when sending objectAtIndex: to an NSString. In your case, arrayLabel isn't an array when you think it is.
If your JSON has an object, your code needs to treat it as an NSDictionary. Likewise for arrays and NSArray and strings and NSString.
In addition to whatever else was going on, you repeatedly refer to "first" but use the index 1. In most C-based programming languages (and others, as well) the convention is that indexes into arrays are 0-based. So, use index 0 to get the first element.

objective-c changing array contents inside for loop

I have an issue that (I think) might have to do with scope, but I'm not sure. I'm trying to do something that I think should be simple, but I am getting a strange result, and I could truly use some advice. I would say I'm an early-objective-c programmer, but not a complete newb.
I have written a function in objective-c that I would like to use to change the key-names in a mutable array of mutable dictionary objects. So, I want to pass in a mutable array of mutable dictionary objects, and return the same mutable array with the same dictionary objects, but with some of the key-names changed. Make sense?
I have tried several log statements in this code, which seem to indicate that everything I'm doing is working, except when the for loop is finished executing (when I try to test the values in the temp array), the array appears to contain only the LAST element in the source array, repeated [source count] times. Normally, this would lead me to believe I'm not writing the new values correctly, or not reading them correctly, or even that my NSLog statements aren't showing me what I think they are. But might this be because of scope? Does the array not retain its changes outside of the for loop?
I have put a fair amount of time into this function, and I have exhausted my bag of tricks. Can anyone help out?
-(NSMutableArray *)renameKeysIn:(NSMutableArray*)source {
/*
// Pre:
// The source array is an array of dictionary items.
// This method renames some of the keys in the dictionary elements, to make sorting easier later.
// - "source" is input, method returns a mutable array
*/
// copy of the source array
NSMutableArray *temp = [source mutableCopy];
// a temporary dictionary object:
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
// These arrays are the old field names and the new names
NSMutableArray *originalField = [NSMutableArray arrayWithObjects:#"text", #"created_at",nil];
NSMutableArray *replacedField = [NSMutableArray arrayWithObjects:#"title", #"pubDate", nil];
// loop through the whole array
for (int x =0; x<[temp count]; x++) {
// set the temp dictionary to current element
[dict setDictionary:[temp objectAtIndex:x]];
// loop through the number of keys (fields) we want to replace (created_at, text)... defined in the "originalField" array
for (int i=0; i<[originalField count]; i++)
{
// look through the NSDictionary item (fields in the key list)
// if a key name in the dictionary matches one of the ones to be replaced, then replace it with the new one
if ([dict objectForKey:[originalField objectAtIndex:i]] != nil) {
// add a new key/val pair: the new key *name*, and the old key *value*
[dict setObject:[dict objectForKey:[originalField objectAtIndex:i]]
forKey:[replacedField objectAtIndex:i]];
// remove the old key/value pair
[dict removeObjectForKey:[originalField objectAtIndex:i]];
}// end if dictionary item not null
}// end loop through keys (created_at, text)
[temp replaceObjectAtIndex:x withObject:dict];
}// end loop through array
// check array contents
for (int a=0; a<[temp count]; a++){
NSLog(#"Temp contents: ############ %#",[[temp objectAtIndex:a] objectForKey:#"pubDate"]);
}
return temp;
} // end METHOD
I think the issue is on the line with:
[dict setDictionary:[temp objectAtIndex:x]];
Since these things are almost all working in pointers (instead of copying contents), every element of your temp array will point to the dict dictionary, which is set to be whatever the latest key's dictionary is. I think setting the actual pointer will fix the issue.
dict = [temp objectAtIndex:x];

How does one populate an NSMutable array of NSMutableSets?

I am using this code in a loop to populate an NSMutable Array of NSMutableSets (of NSString objects). The index of the NSSet is based on the length of the word.
// if set of this length not initialized yet, initialize set.
wordIndex = [NSString stringWithFormat:#"%d", currentWordLength];
if ([myWordArray objectForKey:wordIndex] == nil)
[myWordArray setObject:[[NSMutableSet alloc] initWithObjects:currentWord, nil] forKey:wordIndex];
else
[[myWordArray objectForKey:wordIndex] addObject:currentWord];
The final intention is to split up an array of words into an array of sets of words grouped by their lengths.
However, I see that [myWordArray count] is 0 after this. Why?
You are confusing the methods of NSMutableDictionary and NSMutableArray: In Objective-C arrays do not have keys but have indexes. If you change the class for myWordArray to NSMutableDicitionary it should work.
Try this, it looks very much like your logic, but (1) it uses NSNumbers as keys, which makes a little more sense, (2) handles the missing set condition more simply, but just adding the set, and (3) breaks up the source lines somewhat for easier debugging...
NSArray *inputStrings = // however these are initialized goes here
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (NSString *currentString in inputStrings) {
NSInteger currentWordLength = currentString.length;
wordIndex = [NSNumber numberWithInt:currentWordLength];
NSMutableSet *wordSet = [result objectForKey:wordIndex];
if (!wordSet) {
wordSet = [NSMutableSet set];
[result setObject:wordSet forKey:wordIndex];
}
[wordSet addObject:currentWord];
}
If you still have an empty dictionary after running this, it might be simpler to watch what's happening by stepping through it.

Removing object from CCArray not working

I'd like to remove a NSNumber from a CCArray. When I use removeObject: to remove a NSNumber of 13 or above nothing gets removed. What's going on here? I've tried using an NSMutableArray and it works fine. Using removeObjectAtIndex: is not an option because my array wont be in numerical order.
CCArray *arr = [CCArray array];
for (NSInteger i = 0; i < 20; i++) {
[arr addObject:[NSNumber numberWithInt:i]];
}
// Outputs arr count 20
CCLOG(#"%#: arr count %i ", NSStringFromSelector(_cmd), [arr count]);
[arr removeObject:[NSNumber numberWithInt:13]];
// Still outputs arr count 20
CCLOG(#"%#: arr count %i ", NSStringFromSelector(_cmd), [arr count]);
I can verify that this seems to be a problem. Although I was able to call removeObject successfully, indexOfObject: returned NSNotFound. I rewrote the test case, and the error was gone. One more reason to stay away from CCArray I suppose.
I suggest to use NSMutableArray in this case. The slightly better performance of CCArray will not be reflected in better framerate, especially not for an array that has 20 elements. Besides, correct code is always better than code that's faster but incorrect.