NSArray to NSMutableArray as random stack - iphone

Just a conceptual description first:
I am reading input from a text file (a list of words) and putting these words into an NSArray using componentsSeparatedByString method. This works.
But I wanted to select the words randomly and then delete them from the array so as to ensure a different word each time. Of course, you cannot change the NSArray contents. So...
I copied the contents of the NSArray into an NSMutableArray and use IT for the selection source. This also works - 269 objects in each array.
To return a word from the NSMutableArray I use the following code:
note- the arrays are declared globally
as
arrMutTextWords = [[NSMutableArray alloc] init]; //stack for words
arrTextWords = [[NSArray alloc] init]; //permanent store for words
-(NSString*) getaTextWord
{
// if the mutable text word array is empty refill
if ([arrMutTextWords count] == 0){
for (int i = 0 ; i < [arrTextWords count]; i++)
[arrMutTextWords addObject:[arrTextWords objectAtIndex:i]];
}
int i = random() % [arrMutTextWords count];
NSString* ptrWord = [arrMutTextWords objectAtIndex:i];
[arrMutTextWords removeObjectAtIndex:i];
return ptrWord;
}
The program crashes during a call to the method above - here is the calling code:
arrTmp is declared globally arrTmp = [[NSArray alloc] init]; //tmp store for words
for (int i = 0 ; i < 4; i++) {
tmpWord = [self getaTextWord];
[arrTmp addObject:tmpWord];
[arrTmp addObject:tmpWord];
}
I'm thinking that somehow deleting strings from arrMutTextWords is invalidating the NSArray - but I can't think how this would occur.

One possible source for problems is your fetching AND removing the NSString object from your list. Removing it releases that NSString instance therefore devalidating your reference.
To be shure to retain a reference you should use this code sequence instead:
NSString * ptrWord = [[[arrMutTextWords objectAtIndex:i] retain] autorelease];
[arrMutTextWords removeObjectAtIndex:i];
return ptrWord;
By the way: You should use
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray: array];
instead of copying all values by hand. While i do not know the implementation of NSMutableArray, i know from times long ago (NeXTstep), that there are several possible optimizations that may speed up basic NSArray operations.
And finally copying this way is much more concise.

Just ran this through XCode and got random words returned, however I skipped the whole for loop and used addObjectsFromArrayfrom NSMutableArray.
NSArray *randomArray = [[NSArray alloc] initWithObjects:#"Paul", #"George", #"John", nil];
NSMutableArray *muteArray = [[NSMutableArray alloc] init];
[muteArray addObjectsFromArray:randomArray];
int i = random() % [muteArray count];
NSString* ptrWord = [muteArray objectAtIndex:i];
[muteArray removeObjectAtIndex:i];
NSLog(#"ptrWord %#", ptrWord); //gave me a different name each time I ran the function.
Hope this clears some things up.

Related

Objective-C and ARC: Why value stored to during its initialization is never read?

I'm using this code with ARC:
NSMutableDictionary *datesDict = [[NSMutableDictionary alloc]init];
NSMutableArray *datesArray = [[NSMutableArray alloc]init];
for (NSString *key in programsArray) {
datesArray = [_onDemandDictionary objectForKey:key];
NSMutableArray *newDates = [[NSMutableArray alloc]init];
int count;
for (count = 0; count <datesArray.count; count++) {
NSMutableDictionary *programsDict = [[NSMutableDictionary alloc]init];
programsDict = [datesArray objectAtIndex:count];
[newDates addObject:[programsDict objectForKey:#"date"]];
}
[datesDict setObject:newDates forKey:key];
}
But when I run the analyzer tool I'm getting value stored to (datesArray and programsDict) during its initialization is never read on lines:
NSMutableArray *datesArray = [[NSMutableArray alloc]init];
programsDict = [datesArray objectAtIndex:count];
Why is this happening how do I get hid of the warning?
Thank you!
The issue is you create a new NSMutableArray and assign it to datesArray at the beginning
NSMutableArray *datesArray = [[NSMutableArray alloc]init];
Then almost immediately after you assign a completely different value to datesArray with
datesArray = [_onDemandDictionary objectForKey:key];
I would just start with
NSMutableArray *datesArray = nil;
It's the same concept for programsDict.
On line 2, you create a new array datesArray.
Then, on line 6 (first line of the for loop), you set a new value to datesArray.
The compiler is just warning you that the line 2 has no effect, and that the code is bugged (in the sense it does not do what you expect).
True, the programsArray could be an empty array, and in this case you want datesArray to just be initialized to use it after the snippet you showed us, but it would be better to make this explicit.
For programsDict, it is even easier: you initialize it with ... alloc] init] then set it to an object of datesArray, making the first operation useless.
You are not using datesArray in your loop, you are simply assigning it values, So either take it nil array like
NSMutableArray* datesArray = nil;
or like
NSMutableArray *datesArray;
to remove waring .

iphone: addObjectsFromArray not adding after reinitializing nsmutable array

i have the following code
inAppKeys = [[MKStoreManager sharedManager] purchasableObjectsDescription ];
NSMutableArray * unremovableArray = [[NSMutableArray alloc] init];
for(int i = 0; i<[inAppKeys count]; i++){
for (int j=0; j< [categories count]; j++) {
NSString * inAppKey = [[categories objectAtIndex:j] valueForKey:#"inAppKey"];
if([inAppKey isEqualToString: [inAppKeys objectAtIndex:i]]){
[unremovableArray addObject:[categories objectAtIndex:j]];
}
}
}
categories = [[NSMutableArray alloc] init];
[categories addObjectsFromArray:unremovableArray];
where categories is nsmutablearray .. the thing is addObjectsFromArray leave the categories empty .. what do i do wrong?
Looks to me like you're referring to [categories count] and [categories objectAtIndex:j] before you even alloc/init categories.
Having re-read your title ("reinitializing") which suggests you've previously inited categories, I'm now assuming that you have a master set of categories that you're trying to reduce to the ones actually purchased. If so, I wouldn't re-use the variable "categories" as that's confusing. (I assume categories was auto-released, or else you've got a leak). How 'bout using unremovableArray instead of leaking it?
I'd also use fast enumerators for clarity and speed...
NSLog(#"categories: %#", categories);
inAppKeys = [[MKStoreManager sharedManager] purchasableObjectsDescription ];
NSLog(#"inAppKeys:%#", inAppKeys);
NSMutableArray * unremovableCategories = [[NSMutableArray alloc] init];
for(NSString* thisAppKey in inAppKeys) {
for (NSDictionary* thisCategory in categories) {
if ([[thisCategory valueForKey:#"inAppKey"] isEqualToString: thisAppKey]){
[unremovableCategories addObject:thisCategory];
break; //having added this category; no reason to continue looking at it
}
}
}
//now use unremovableCategories...

SQL select statement equivalent for query on plist

I am trying to figure out the best approach for queries on a plist. Thus, attempting at making an sql equivalent to "SELECT * FROM sometable WHERE somecol = someval AND someothercol = someotherval AND ".... so on and soforth ... using a plist as the sql table equivalent. Coming from Ruby and mysql, this just seems like a lot of code for one simply query. The results come back as expected (at least at first run they did, I have not tested this rigorously) with no errors.
So here is the question: Is there some simple method hiding in the docs somewhere that would make this less clunky?
and if not what is a better approach?
EPFramework.m
// LOAD PLIST AND FILTER MULTIPLE TIMES
-(NSMutableArray *)loadPlistAndFilterMultipleTimes:(NSString *)plist ArrayOfKeys:(NSArray *)arrayOfKeys ArrayOfKeyValues:(NSArray *)arrayOfKeyValues
{
// set the array counts
int arrayOfKeysCount = [arrayOfKeys count];
int arrayOfKeyValuesCount = [arrayOfKeyValues count];
// initialize the array to return
NSMutableArray *arrayFilteredResults = [[[NSMutableArray alloc] init] retain];
// qualify the search
if(arrayOfKeysCount == arrayOfKeyValuesCount && arrayOfKeysCount > 0 && arrayOfKeyValuesCount > 0)
{
// get the plist
NSString *fullFileName = [NSString stringWithFormat:#"%#.plist", plist];
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:fullFileName];
// put the plist records into an array
NSArray *arrayOfDictionaryItemsInPlist = [[NSMutableArray alloc] initWithContentsOfFile:path];
// load our dynamic array for mutability throughout the loops
NSMutableArray *arrayFiltered = [[[NSMutableArray alloc] initWithArray:arrayOfDictionaryItemsInPlist] retain];
// build an array of the final results to return
for(int i=0; i < arrayOfKeysCount; i ++)
{
// initialize this loops search criteria
NSString *key = [arrayOfKeys objectAtIndex:i];
id value = [arrayOfKeyValues objectAtIndex:i];
// set the filter
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == %#", key, value];
// filter the result
arrayFilteredResults = [[[NSArray alloc] initWithArray:[arrayFiltered filteredArrayUsingPredicate:predicate]] retain];
}
} else {
NSLog(#"arrOfKeys count does not match arrayOfKeyValues count"); // the search did not qualify
}
// return the results
return arrayFilteredResults;
// release the allocated memory
[arrayFilteredResults release];
}
IndexController.m
NSArray *arrayOfKeys = [NSArray arrayWithObjects:
[NSString stringWithString:#"recordset"],
[NSString stringWithString:#"ep_object_attribute_id"],
nil];
NSArray *arrayOfKeyValues = [NSArray arrayWithObjects:
[NSString stringWithString:#"1778587279"],
[NSNumber numberWithInt:133],
nil];
NSMutableArray *arrayOfResult = [epFrameWork loadPlistAndFilterMultipleTimes:#"FormEntries" ArrayOfKeys:arrayOfKeys ArrayOfKeyValues:arrayOfKeyValues];
NSLog(#"arrayOfResult: %#", arrayOfResult);
Here's the link for fmdb. Should be quick to pick it up, be faster and you get a real database instead of simulating one with plists :)
https://github.com/ccgus/fmdb
Hope that helps

Problem with arrays (add and copy)

I've got a problem. I'm trying to sort the items from 1 array (name: mps) into an other array (name: totalArray), but every item should be in a new array. In short: the items in mps should move into a new array, but every item should be in a new array, so the totalArray is an array of arrays. After that, totalArray should be copied back into mps.
To clarify, here is the code:
NSMutableString *oud = [[NSMutableString alloc] init];
NSMutableString *nieuw = [[NSMutableString alloc] init];
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
[tempArray removeAllObjects];
NSMutableArray *totalArray = [[NSMutableArray alloc] init];
[totalArray removeAllObjects];
tempArray = [[NSMutableArray alloc] init];
[tempArray removeAllObjects];
for (int j = 0; j < [mps count]; j++)
{
nieuw = [[mps objectAtIndex:j] valueForKey:#"ConfigurationAlias"];
if (j != 0)
{
if ([oud rangeOfString:nieuw].location == NSNotFound)
{
NSLog(#"ADDED!");
[totalArray addObjectsFromArray:tempArray];
[oud setString:nieuw];
[tempArray removeAllObjects];
[tempArray addObject:[mps objectAtIndex:j]];
}
else
{
[tempArray addObject:[mps objectAtIndex:j]];
}
}
else {
[oud setString:nieuw];
[tempArray addObject:[mps objectAtIndex:j]];
}
}
//En de array weer laden in de tabel
//NSLog(#"%#", totalArray);
NSLog(#"%i", [mps count]);
[mps removeAllObjects];
NSLog(#"REMOVED!!!");
NSLog(#"%i", [mps count]);
[mps addObjectsFromArray:totalArray];
NSLog(#"Added totalArray: %i", [totalArray count]);
NSLog(#"%i", [mps count]);
[tempArray release];
[totalArray release];
[tabelView reloadData];
}
Now, here comes my problem:
The log gives the line 2011-04-07 10:02:46.368 Fleet Manager[901:40b] ADDED! 11 times, then it posts 16 (mps count, this is correct), then the line REMOVED!, then 0 (mps count, also correct), but then the line: Added totalArray: 15. This can't be correct, it added 11 times but it counts 15?
This a problem for me, in the first place because it's plain incorrect, but the tableview gets screwed up as well because of the section-amount not being correct either (15 sections). Anybody has any idea what's going on?
EDIT: all the objects in mps are dictionaries, and all of them have a value for the key "ConfigurationAlias".
First, I would like to suggest using debugging mode instead of bunch of NSLog functions to track down what's actually happening inside your objects and variables. Try using breakpoints. Set one for [totalArray addObjectsFromArray:tempArray]; line so every time this line of code is reached the execution would stop and let you look around, check different fields' states inside totalArray and tempArray objects.
Concerning your problem... Since you are using addObjectsFromArray: that means that passed array may pass not one but several objects (thus the plural in method name). I guess one of those "11 times" added more than one element - that should explain the actual count being more than the times you've called this method. Again, please check tempArray contents.
Good luck!
If you want to end up with an array of arrays, you need to be allocating temp arrays inside the loop. It also seems like you are overdoing it with clearing out the arrays.
It appears you are trying to add the value from mps to totalArray regardless of the conditions, so it would make sense to only do that in one place (outside of the conditions).
Also, if you are not mutating the arrays (such as tempArray), you can use NSArray instead, which should be smaller and faster.
Finally, you should be able to assign the new array to mps instead of doing the expensive array copying. Not sure if that is somehow referenced as the table delegate, though.
I would try something like:
NSMutableString *oud = [[NSMutableString alloc] init];
NSMutableString *nieuw = [[NSMutableString alloc] init];
NSMutableArray *tempArray;
NSMutableArray *totalArray = [[NSMutableArray alloc] init];
for (int j = 0; j < [mps count]; j++)
{
tempArray = [[NSMutableArray alloc] init];
[totalArray addObject:tempArray];
[tempArray release];
nieuw = [[mps objectAtIndex:j] valueForKey:#"ConfigurationAlias"];
if (j != 0)
{
if ([oud rangeOfString:nieuw].location == NSNotFound)
{
NSLog(#"ADDED!");
[oud setString:nieuw];
}
}
else {
[oud setString:nieuw];
}
}
NSLog(#"%i", [mps count]);
[mps removeAllObjects];
NSLog(#"REMOVED!!!");
NSLog(#"%i", [mps count]);
[mps addObjectsFromArray:totalArray];
NSLog(#"Added totalArray: %i", [totalArray count]);
NSLog(#"%i", [mps count]);
[totalArray release];
[tabelView reloadData];
}
You should
sort the base array in a tempArray
create an empty resultArray
iterate through tempArray and for each item create an itemArray containing the item on the first position and add this array to resutArray
don't forget to release all alloc-ed temporary data
Check this out for sorting easily: Sorting arrays via Apple

Filter a NSMutableArray index beyond bounds Problem

I am trying to filter a NSMutableArray. Search array for items by certain country. I have tried NSpredicate which works great however I need to re-use the original array which I cannot with NSpredicate. So I am trying the below code.
Q. What is the best way to filter an NSMutableArray keeping the original array intact?
//The following code works
filteredArray = [[NSMutableArray alloc] init];
unsigned int i;
for (i = 0; i < [appDelegate.arrayToBeFiltered count]; i++) {
id session = [appDelegate.ads objectAtIndex: i];
id Country = [[appDelegate.arrayToBeFiltered objectAtIndex: i] TheCountry];
[filteredArray addObject: session];
However when I add the if statement as below I get index beyond bounds
filteredArray = [[NSMutableArray alloc] init];
unsigned int i;
for (i = 0; i < [appDelegate.arrayToBeFiltered count]; i++) {
id session = [appDelegate.ads objectAtIndex: i];
id Country = [[appDelegate.arrayToBeFiltered objectAtIndex: i] TheCountry];
if (Country == #"United States"){
[filteredArray addObject: session];
}
}
Use - (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate
As a side-note, in the code above you probably mean to do this [Country isEqualToString: #"United States"]
As an extra side note - don't capitalise variables and method names. Just a style thing but capitalisation is usually reserved for Class names