Setting Values in NSArray - iphone

Looked around for a while and couldn't find an answer to this, but it seems like a pretty simple thing. I want to create an NSArray that has room for 100 objects (Doors) and then just loop through to create a new door for each index in the array. I couldn't find a way to do this without manually writing the code for 100 new doors when I init the NSArray. I know I could do this by just creating an NSMutableArray and using addObject but I've heard NSArrays are much faster and I'd like to know how to do this for future reference.
Here's what I'm basically trying to do:
NSArray *doors = [[NSArray alloc]init]; //with size 100?
for (int i = 0; i < [doors count]; i++)
[[doors objectAtIndex:i] = [[Door alloc]init]];

if you are going to add objects to an array inside of a loop, then NSMutableArray is the correct object to use.
To create 100 doors:
NSMutableArray *doors = [NSMutableArray new];
for (int i=0; i < 100; i++){
[doors addObject:[[Door alloc]init]];
}
Now you have an mutable array with 100 Door Objects.
If you have no need to later modify the array, you can convert it back to
an immutable array for processing like this:
NSArray *newDoorList = [NSArray arrayWithArray:doors];
There may be some minor performance hits between mutable and non mutable arrays, but you will not notice them with just a few hundred objects - at least that has been my experience.
hope that helps. good luck.

Create an NSMutableArray and then use [array copy] to get a non-mutable NSArray.

See this answer for some options, basically create an NSMutable array and use that to create your NSArray...
http://www.cocoabuilder.com/archive/cocoa/190315-nsmutablearray-to-nsarray.html#190321

NSMutableArray is a dynamic array, and you don't need to predefine a size. As Aleph suggests, use an NSMutableArray and append your door object 100x. Like this:
NSMutableArray *myArray = [NSMutableArray arrayWithCapacity:100];
for ( int i = 0; i < 100; i++ ) {
[myArray addObject: [[Door alloc] init]];
}
NSArray *filledArray = [NSArray arrayWithArray:myArray];
Just edited to include the arrayWithCapacity: method. Per this SO thread, if you know the exact number you need, it should just allocate the memory all at once. Besides that, NSArray vs. NSMutableArray isn't going to show that much difference speed-wise. ( Until proven wrong. O_o )
Disadvantage of using NSMutableArray vs NSArray?

Related

Mutable deep-copy of a NSMutableDictionary

First of all I found something similar:
deep mutable copy of a NSMutableDictionary
but it didn't solve my problem.
I have a NSMutableDictionary as a template.
NSMutableDictionary *mutableDictionaryTemplate = [NSMutableDictionary dictionaryWithObjectsAndKeys: #"obj1, #"key1",#"obj2, #"key2",nil];
Now I would like to copy this dictionary, then change some parts and later save it in a NSMutable Array.
NSMutableArray *savedIterations = [NSMutableArray new];
//some loop that normally would change the objects added
int i=0;
for (i < 5){
NSMutableDictionary *copiedDictionary = [mutableDictionaryTemplate copy];
[copiedDictionary setObject:#"obj3" forKey:#"key3"];
[savedIterations addObject:copiedDictionary];
i++;
}
My problem is that once I copy the NSMutableDictionary "mutableDictionaryTemplate" it no longer is mutable. But I need to copy it because otherwise I will have the same NSMutableDictionary at every index of my NSMutableArray "savedIterations" (at least I think so). I tried mutable copy as well but there I change the "mutableDictionaryTemplate" when I change "copiedDictionary". I think I have something messed up with what I have to copy and what not and how to copy it correctly.
It would be great if someone could point me into the right direction.
You could try this:
NSMutableDictionary *destinationDictionary = (NSMutableDictionary *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (__bridge CFPropertyListRef)(sourceDictionary), kCFPropertyListMutableContainers));

Copy an Array - Bad Design?

I learn objective-C from Stanford iTunes and i wonder how i should copy a NSMutableArray to NSArray without initialization. I mean:
Is this is correct? with "lazy initialization".
-(void)copyAnArray:(NSMutableArray*)listOfElements {
if(privateElementsLists == nil)
privateElementsLists = [[NSArray alloc] initWithArray:listOfElements copyItems:YES];
else
privateElementsLists = listOfElements;
}
is this a bad design?
I want to addobjects to mutable array in one class, and then when i'm finish copy entire NSMutableArray to NSArray.
And another question: Why i have to use copyItems:YES when I use initWithArray? And what's deep copy?
You can copy a mutable array to a new array with initWithArray: or this way:
privateElementsLists = [NSArray arrayWithArray:listOfElements];
then you are creating a new array where each of its elements is the same object that figures in the original array. If you write:
privateElementsLists = [NSArray arrayWithArray:listOfElements copyItems:YES];
then the new array have, for each element, a copy of the element in original array. They are not the same object but a copy. Of course, that objects have to be able to respond to copy.
You can even do this:
privateElementsLists = (NSArray*) listOfElements ;
Then the array is exactly the same as the original one. No new array here. But as you have casted it with NSArray pointer class, you can use it as if it is a NSArray instead of a NSMutableArray. As you know, every NSMutableArray is a NSArray (inherited class).
As Joseph DeCarlo stated, you don't need to copy NSMutableArray to NSArray if the only thing you do is to create the array in one place to use it somewhere else. For example this statement is valid:
NSArray* newArray = [NSMutableArray array];
Or in the code:
-(NSArray*)returnAnArray
{
NSMutableArray* editableArray = [NSMutableArray array];
[editableArray addObject:[[NSObject alloc] init]]; //an exemplary object added to the array
return editableArray;
}
That said, however, in some specific cases casting NSMutableArray to NSArray may not be safe, e.g. if the original array was stored in an instance variable. Adding or removing objects to/from that array may cause a crash if the returned array is enumerated at the same time. For example:
-(void)createArray
{
self->editableArray = [NSMutableArray array]; // instance variable: NSMutableArray* editableArray
}
-(void)addObjectToArray
{
[self->editableArray addObject:[[NSObject alloc] init]];
}
-(NSArray*)getArray
{
return self->editableArray;
}
-(void)enumerateArray
{
for(NSObject obj in [self getArray])
{
// do something with obj
}
}
If addObjectToArray is called at the same time as enumerateArray (e.g. from a background thread) the application will crash because the underlying array is changing while it is being enumerated. It doesn't matter that it was returned as NSArray*. In a case like this you would need to either add #synchronized to synchronize access to the same object by multiple threads, or copy the entire array with arrayWithArray: as suggested. Note, however, that the documentation doesn't say if arrayWithArray: is thread safe so I would add #synchronized around the call to arrayWithArray: anyway.

Creating a Two-Dimensional Array with Concrete Positions

I need to create a custom array:
In php I would define as follows:
$myarray[100][80] = 1;
But I don't know how to do it in objective-c...
I don't need an array [0][0],[0][1],[0][2], ... I only need concrete positions in this array [80][12], [147][444], [46][9823746],...
The content of these positions always will be = 1;
for this you would use a dictionary rather than an array as they are always 0,1,2 keyed so something along the lines of:
NSNumber *one = [NSNumber numberWithInt:1];
NSString *key = #"80,12";
NSDictionary *items = [NSDictionary dictionaryWithObject:one forKey:key];
Then to pull them out again you would use the objectForKey: method.
You cannot put ints directly into arrays or dictionaries that's why it is wrapped in the NSNumber object. To access the int after getting the NSNumber out of the dictionary you would use something like:
NSNumber tempNum = [items objectForKey:key];
int i = tempNum.intValue;
See the docs here for a full explanation of the NSDictionary class. Hope this helps...
I an not a PHP master but I believe in php arrays are not real arrays they are hash tables right?
Anyway, I think you are looking for NSDictionary or NSMutableDictionary class.
That looks more like a bitset than an array.
Allocating so many cells for that seems useless, so maybe you could revert the problem, and store the positions in an array.
Well in objective c we can use NSMutableArray to define 2-D arrays.
See the following code, it might help you
NSMutableArray *row = [[NSMutableArray alloc] initWithObjects:#"1", #"2", nil];
NSMutableArray *col = [[NSMutableArray alloc] init];
[col addObject:row];
NSString *obj = [[col objectAtIndex:0] objectAtIndex:0];
NSLog(#"%#", obj);

NSMutableArray when using arrayWithArray the mutable array becomes the right size, but the object are all out of scope

NSArray * ComicArray = [TCSDataBase fetchManagedObjectsForEntity:#"ComicDB" withPredicate:nil];
[ComicArray retain];
arrayOfComics = [NSMutableArray arrayWithArray:ComicArray];
[[arrayOfComics valueForKey:#"Name"] sortUsingSelector:#selector(caseInsensitiveCompare:)];
[ComicArray release];
Why are all the object in the arrayOfComics out of scope?
EDIT: I tried doing this:
NSArray * ComicArray = [TCSDataBase fetchManagedObjectsForEntity:#"ComicDB" withPredicate:predicate];
arrayOfComics =[[NSMutableArray alloc] init];
for (int i = 0; i < [ComicArray count]; i++) {
[arrayOfComics addObject:[ComicArray objectAtIndex:i]];
}
[[arrayOfComics valueForKey:#"Name"] sortUsingSelector:#selector(caseInsensitiveCompare:)];
All the objects in arrayOfComics are still out of scope....
EDIT: This works, the objects in arrayOfComicsTest are NOT "out of scope". I am not sure why this works yet when i do arrayOfComics they are out of scope. arrayOfComics is a class variable NSMutableArray * arrayOfComics in the .h. It is not used anywhere until this point.
NSMutableArray * arrayOfComicsTest = [NSMutableArray arrayWithArray:ComicArray];
NSArray *comicArray = [TCSDataBase fetchManagedObjectsForEntity:#"ComicDB" withPredicate:nil];
NSArray *sortedComicNamesArray = [[comicArray valueForKey:#"Name"] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
Note that the latter array only has the names of the comics sorted. It does not contain a sorted list of your comics.
BTW, if you want a sorted list for your comics (not just the names) simply create the proper predicate to use in the -fetchManagedObjectsForEntity:withPredicate: method.
Try this....still i m not getting why u r not using executeFetchRequest
NSArray * ComicArray = [TCSDataBase fetchManagedObjectsForEntity:#"ComicDB" withPredicate:nil];
[ComicArray retain];
arrayOfComics = [NSMutableArray arrayWithArray:ComicArray];
[[arrayOfComics valueForKey:#"Name"] sortedUsingSelector:#selector(caseInsensitiveCompare:)];
You have to retain your array "arrayOfComics". Put a break point in your code and immediately when it is created, all the objects will be available. But if you want to use that array in some other method, all the objects go out of scope as you are not retaining.
Else it will have the scope upto the method in which it is assigned.
After correction, your code must look like:
NSArray * ComicArray = [TCSDataBase fetchManagedObjectsForEntity:#"ComicDB" withPredicate:nil];
[ComicArray retain];
arrayOfComics = [NSMutableArray arrayWithArray:ComicArray];
[arrayOfComics retain];
[[arrayOfComics valueForKey:#"Name"] sortUsingSelector:#selector(caseInsensitiveCompare:)];
[ComicArray release];
Please note that if you want to reassign arrayOfComics, you have to release and then assign.
When you say the objects are "out of scope" I presume you mean you can't see them in the debugger. If this is the case, stop worrying about it as it happens quite a lot, it's just an implementation feature. In particular, core data doesn't always fetch an actual object till it's needed (read about faulting in the Core Data Programming Guide). If you right click on the array in the debugger display and select "print description to console", it'll print the array and all its objects quite happily.
Your code does have a problem though. This line:
[[arrayOfComics valueForKey:#"Name"] sortUsingSelector:#selector(caseInsensitiveCompare:)];
is nonsense. First it gets an array of the name keys of your objects and then it tries to sort it (which may fail unless the array of names happens to be mutable). Then it throws away the result.
At the time I was having a bunch of problems because I did not fully understand the difference between mutable and non mutable arrays, as well as the various return values from array operations.
I eventually fixed this by making a bunch of changes.
Thanks to everyone that provided help.

How can i get Original order of NSDictionary/NSMutableDictionary?

i have created NSMutableDictionary with 10 keys.Now i want to access NSMutableDictionary keys in a same order as it was added to NSMutableDictionary (using SetValue:* forKey:* );
How can i achieve that ?
If you absolutely must use a dictionary container, you have to use a key that is sortable by the order in which you add key-value pairs. Thus, when creating your dictionary, you use a key that is an auto-incrementing integer or similar. You can then sort on the (integer) keys and retrieve the values associated with those keys.
If you do all of that, however, you may as well just use an NSMutableArray and add values to the array directly! It will be much faster and require less code. You just retrieve objects in order:
for (id obj in myArray) { /* do stuff with obj... */ }
NSMutableDictionary can't do that. Take a look at e.g. Matt Gallaghers OrderedDictionary.
I wrote a quick method to take a source array (of objects that are all out of order) and a reference array (that has objects in a desired (and totally arbitrary) order), and returns an array where the items of the source array have been reorganized to match the reference array.
- (NSArray *) reorderArray:(NSArray *)sourceArray toArray:(NSArray *)referenceArray
{
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
for (int i = 0; i < [referenceArray count]; i++)
{
if ([sourceArray containsObject:[referenceArray objectAtIndex:i]])
{
[returnArray addObject:[arrReference objectAtIndex:i]];
}
}
return [returnArray copy];
}
Note that this is very fragile. It uses NSArray's containsObject: method, which ultimately will call NSObject's isEqual:. Basically, it should work great for arrays of NSStrings, NSNumbers, and maybe NSDates (haven't tried that one yet), but outside of that, YMMV. I imagine if you tried to pass arrays of UITableViewCells or some other really complex object, it would totally sh*t itself, and either crash or return total garbage. Likewise if you were to do something like pass an array of NSDates as the reference array and an array of NSStrings as the source array. Also, if the source array contains items not covered in the reference array, they'll just get discarded. One could address some of these issues by adding a little extra code.
All that said, if you're trying to do something simple, it should work nicely. In your case, you could build up the reference array as you are looping through your setValue:forKey:.
NSMutableArray *referenceArray = [[NSMutableArray alloc] init];
NSMutableDictionary *yourDictionary = [[ NSMutableDictionary alloc] init];
for (//whatever you are looping through here)
{
[yourDictionary setValue://whatever forKey:key];
[referenceArray addObject:key];
}
Then, when you want to loop over your items in the order they came in, you just
for (NSString *key in [self reorderArray:[myDict allKeys] toArray:referenceArray])
Actually you have a reference array in order manner then why you have to add to one more array.So i guess this approach is not good.Please consider my opinion.
Although #GenralMike 's answer works a breeze, it could be optimized by leaving off the unnecessary code as follows:
1) Keep an array to hold reference to the dictionary keys in the order they are added.
NSMutableArray *referenceArray = [[NSMutableArray alloc] init];
NSMutableDictionary *yourDictionary = [[ NSMutableDictionary alloc] init];
for (id object in someArray) {
[yourDictionary setObject:object forKey:someKey];
[referenceArray addObject:someKey]; // add key to reference array
}
2) Now the "referenceArray" holds all of the keys in order, So you can retrieve objects from your dictionary in the same order as they were originally added to the dictionary.
for (NSString *key in referenceArray){
//get object from dictionary in order
id object = [yourDictionary objectForKey:key];
}