Initializing multidimensional NSArray for iPhone devel - iphone

I'm starting with iPhone development and I need to use a multidimensional array.
I initialize it using:
NSArray *multi=[NSArray
arrayWithObjects:[NSMutableArray arrayWithCapacity:13],
[NSMutableArray array],nil];
But when I try to assign values to n-th cell like this:
[[multi objectAtIndex:4] addObject:#"val"];
The app hangs because index 4 is beyond the bounds [0 .. 1].
Which is the correct way to init my multi-array? Thanks in advance and greetings.

I guess you want to create a NSMutableArray of NSMutableArrays:
NSMutableArray *multi = [NSMutableArray arrayWithCapacity: 13];
for (int i = 0; i != 13; i++)
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity: 10];
[multi insertObject: array atIndex: 0];
}
After that, your call is valid.
EDIT: as a side note, capacity != count, as would be in .NET or C++'s STL if you know these.

What you did is creating an array containing two objects: two other arrays. You're actually asking for the 5th object within this "super-array", which won't work because there is none.
By the way, even if you create an array specifying a capacity, it is still empty. Specifying a capacity merely allocs enough memory for the array to hold at least the given number of objects. If you add no objects, it would still make your app crash if you'd ask for, say, the 10th object.

Related

NSSet how to extract object randomly?

I am not sure about how NSSet's anyObject work. What does it mean that "The object returned is chosen at the set’s convenience" (from the NSSet class reference) ?
Further, how can I best extract objects randomly from a NSSet? I was thinking about getting allObjects in an array and then myArray[arc4random_uniform(x)] where x is the number of objects in the array.
Quote from NSSet Class Reference:
The object returned is chosen at the set’s convenience—the selection is not guaranteed to be random.
For "randomness", convert the NSSet to an NSArray using [theSet allObjects].
Next, pick any object randomly using arc4random_uniform().
Usually, NSSet instances are created with a CFHash backing, so they almost always return the first object in that hash, as it is the fastest to look up. The reason it says
The object returned is chosen at the set’s convenience—the selection is not guaranteed to be random.
Is because you don't always know it will have a backing array. For all you know, the NSSet instance you have has a NSDictionary backing it, or some other similar data structure.
So, in conclusion, if you need a random object from a NSSet, don't use -anyObject, instead use allObjects: and then shuffle that array.
The documentation reads that anyObject returns
One of the objects in the set, or nil if the set contains no objects.
The object returned is chosen at the set’s convenience—the selection
is not guaranteed to be random.
Most likely there is some deterministic algorithm at work.
The most reliable thing to do would be, as you suggest, to create an NSArray using the NSSet method allObjects, and then choose a random element from that with arc4random() % N where N is the count of the NSArray.
I use arc4random() and two mutable arrays to get a random and unique set of objects:
NSMutableArray *selectionPool = ...;
int numberOfObjectsToSelect = x;
NSMutableArray *selectedObjects = [[NSMutableArray alloc] initWithCapacity:numberOfObjectsToSelect];
int modulus = selectionPool.count - 1;
for (int i = 0; i < numberOfObjectsToSelect; i++) {
int j = arc4random() % (modulus--);
[selectedObjects addObject:[selectionPool objectAtIndex:j]];
[selectionPool removeObjectAtIndex:j];
}
I'm not sure how efficient it would be for large collections, but it's worked for me with collections that number in the low 100s of objects.

2D NSMutable arrays and some musings on NSMutable arrays

I am trying to comprehend how I can create multi-dimensional NSMutable arrays in general. I have come across a few solutions but haven't been able to make them work for me, so I am not sure of their validity. Now if only someone here can help me understand how to create 2-D NSMutable arrays better that would be great!
Moving to the next question, I am not sure when I should summon NSArray/NSMutableArray vs simply using a C array. In the particular case I am dealing with, I have a fixed data type
that I want to use (boolean values) and these are clearly not objects. NS and NSMutableArray are meant to hold objects, if I am not mistaken. So is this a good idea to use a regular C array vs NSMutable array?
Adding a final twist to the question on creating 2D arrays, is using NSMatrices a better alternative or even an option than creating 2D NSMutable arrays?
Thanks and major high fives to all those who read and answer this!
To create 2D array using NSMutableArrays you would need to do the following:
// Create the 2D array
// array2D is created autoreleased. It should be retained somewhere
// to keep it around.
NSMutableArray* array2D = [NSMutableArray array];
// Add a NSMutableArray to array2D for each row
NSUInteger countRows = 8; // Or whatever value you need
for (NSUInteger i = 0; i < countRows; i++) {
NSMutableArray* row = [NSMutableArray array];
[array2D addObject:row];
}
Note that you can add additional rows to array2D at any time. Also each row starts out
with size 0 and is empty. You can add different number of elements to each row so it
is a jagged 2D array rather than something more like a matrix which would be fixed size
(i.e. M rows x N columns.)
To set a value at a specific row and column you would do the following:
NSUInteger rowIndex = 5;
NSUInteger columnIndex = 7;
NSNumber* value = [NSNumber numberWithInt:11];
// Get the 6th row
// Make sure there are 6 rows
NSUInteger countRows = array2d.count;
if (countRows >= (rowIndex + 1)) {
NSMutableArray* row = (NSMutableArray*)[array2d objectAtIndex:rowIndex];
// Get the 8th column
// Make sure there are 8 columns
NSUInteger countColumns = row.count;
if (countColumns >= (columnIndex + 1)) {
// Set the value
[row setObject:value atIndex:columnIndex];
}
}
You can store C types in NSMutableArrays by wrapping them in ObjectiveC objects.
A number can be translated into an object using NSNumber. NSNumber can also wrap boolean
values. Pointers to C structs can be wrapped using NSValue objects. You can also create
NSValue objects that wrap specific Cocoa types, e.g. CGRect.
int intValue = 1;
NSNumber* number = [NSNumber numberWithInt:intValue];
BOOL boolValue = NO;
NSNumber* number = [NSNumber numberWithBool:boolValue];
NSArrays are not modifiable. If you need to add or remove objects to an array, you should you an NSMutableArray. Otherwise use a NSArray.
NSMutableArrays and NSArrays retain the objects that are added to them, taking ownership of them. When an object is removed from an NSMutableArray it is released so that it is cleaned up. When you release an NSArray or an NSMutableArray that you no longer require, they will clean up any objects that you have added to the array. This makes memory management of
objects within arrays much easier.
You can't add nil objects to NSArrays and NSMutableArrays.
NSMutableArray is dynamically resizing whilst C arrays are not. This makes it much easier
to deal with adding and removing objects to the array.
I would use a C array for a group of C types, e.g. ints that is fixed size and
whose values are known at compile time:
int knownConstantValues[] = { 1, 2, 3 };
You might need to use a C array to pass data to a library with a C API, e.g. OpenGL.
Hope this helps.

Dynamically create arrays at runtime and add to another array

I am working on a small isometric engine for my next iPhone game. To store the map cells or tiles I need a 2 dimensionel array. Right now I am faking it with a 1-dimensionel, and it is not good enough anymore.
So from what I have found out looking around the net is that in objective-c I need to make an array of arrays.
So here is my question: How do I dynamicly create arrays at runtime based on how many map-rows I need?
The first array is easy enough:
NSMutableArray *OuterArray = [NSMutableArray arrayWithCapacity:mapSize];
now I have the first array that should contain an array for each row needed.
Problem is, it can be 10 but it can also be 200 or even more. So I dont want to manually create each array and then add it. I am thinking there must be a way to create all these arrays at runtime based on input, such as the chosen mapsize.
Hope you can help me
Thanks in advance
Peter
I think this previous question should help.
2d arrays in objective c
Nothing to do with me. I have never owned an iphone or tried to write code for one.
The whole point of NSMutableArray is that you don't care. Initialize both dimensions with an approximate size and then add to them. If your array grows beyond your initial estimate the backing storage will be increased to accomodate it. This counts for both your columns (first order array), and rows (second order array).
EDIT
Not sure what you meant in your comment. But this is one way to dynamically create a 2-dimensional mutable array in Objective-C.
NSUInteger columns = 25 ; // or some random, runtime number
NSUInteger rows = 50; // once again, some random, runtime number
NSMutableArray * outer = [NSMutableArray arrayWithCapacity: rows];
for(NSUInteger i; i < rows; i++) {
NSMutableArray * inner = [NSMutableArray arrayWithCapacity: columns];
[outer addObject: inner];
}
// Do something with outer array here
NSMutableArray can hold as many elements as you want to add to it. (Based on available heap though).
All you have to do is when you want to add an element(Array) to this mutable array you can add it using the addObject method.
So you create a MutableArray as follows:
NSMutabaleArray *outerArray = [NSMutableArray array]; // initially contains 0 elements.
[outerArray addobject:<anotherArray>];
From your rejection of the other answers I think you don't know how to add them in a loop, or am I wrong?
Try:
for (i = 0; i < mapSize; i++)
[outerArray addObject: [[NSMutableArray alloc] init]];
or, if you know or can estimate the size of the second dimension:
for (i = 0; i < mapSize; i++)
[outerArray addObject: [[NSMutableArray alloc]
initWithCapacity: your_2nd_d_size]];
Now, how you fill the arrays, i.e. where you get the contents depends on you. In one or more loops, you do:
[(NSMutableArray *)[outerArray objectAtIndex: i]
addObject: your_current_object];

Objective-C, How can I produce an array / list of strings and count for each?

My aim is to produce an array, which I can use to add section headers for a UITableView. I think the easiest way to do this, is to produce a sections array.
I want to create section headers for dates, where I'll have several or no rows for each.
So in my populate data array function, I want to populate a display array. So record 1, look for the first date in my display array, create a new array item if it doesn't exist, if it does exist add 1 to the count.
So I should end up with something like this.
arrDisplay(0).description = 1/June/2001; arrDisplay(0).value = 3;
arrDisplay(1).description = 2/June/2001; arrDisplay(1).value = 0;
arrDisplay(2).description = 3/June/2001; arrDisplay(2).value = 1;
arrDisplay(3).description = 5/June/2001; arrDisplay(3).value = 6;
My question is how do I create and use such an array with values, where I can add new elements of add to the count of existing elements and search for existing elements ?
I think, if i understand you, an NSMutableDictionary would work. (as NR4TR said) but, i think the object would be the description and the key would be the count. you could check for the key and get the count in the same gesture. if the return value of objectForKey is nil, it doesn't exist.
NSMutableDictionary *tableDictionary = [[NSMutableDictionary alloc] init];
NSString *displayKey = #"1/June/2001";
NSNumber *displayCount = [tableDictionary objectForKey:displayKey];
if (displayCount != nil) {
NSNumber *incrementedCount = [[NSNumber alloc] initWithInteger:[displayCount integerValue] + 1];
[tableDictionary removeObjectForKey:displayKey];
[tableDictionary setValue:incrementedCount
forKey:displayKey];
[incrementedCount release];
}
else {
NSNumber *initialCount = [[NSNumber alloc] initWithInteger:1];
[tableDictionary setValue:initialCount
forKey:displayKey];
[initialCount release];
}
EDIT: Hopefully this isn't pedantic, but I think a couple pointers will help.
Dictionaries, Sets, and Arrays all hold objects for retrieval. The manner of holding and retrieval desired drives the decision. I think of it based on the question 'what is the nature of the information that I have when I need an object being held?'
NSDictionary and NSMutableDictionary
Hold n objects per key. (I think...I haven't had to test a limit, but i know you can get an NSSet back as a value.)
KEY is more important than INDEX. I don't think of dictionaries as ordered. they know something and you need to ask the correct question.
NSArray and NSMutableArray
hold n objects in order.
INDEX is most important bit of information. (you can ask for the index of an object but, even here, the index is the important part)
you will typically drive table views with an array because the ordered nature of the array fits.
NSSet, NSMutableSet, and NSCountedSet
A collection of objects without order.
You can change any of these into the other with something like [nsset setFromArray:myArray];
and all of these things can hold the other as objects. I think an array as your top level is the correct thinking, but beyond that, it becomes an issue of implementation
Try array of dictionaries. Each dictionary contains two objects - section title and array of section rows.
If you want to have a description AND a rowcount then you can either create a class with those two properties and generate an NSArray of objects with that class or instead of all that you can just use an NSDictionary to store key/value lookups.
I think NSCountedSet is closest to what you want. It doesn't have an intrinsic order, but you can get an array out of it by providing a sort order.

Objective C: Create arrays from first array based on value

I have an array of strings that are comma separated such as:
Steve Jobs,12,CA
Fake Name,21,CA
Test Name,22,CA
Bill Gates,44,WA
Bill Nye,21,OR
I have those values in an NSScanner object so that I can loop through the values and get each comma seperated value using objectAtIndex.
So, what I would like to do, is group the array items into new arrays, based on a value, in this case, State. So, from those, I need to loop through, checking which state they are in, and push those into a new array, one array per state.
CA Array:
Steve Jobs,12,CA
Fake Name,21,CA
Test Name,22,CA
WA Array:
Bill Gates,44,WA
OR Array:
Bill Nye,21,OR
So in the end, I would have 3 new arrays, one for each state. Also, if there were additional states used in the first array, those should have new arrays created also.
Any help would be appreciated!
You can use a NSMutableDictionary of NSMutableArrays - if the state encountered isn't yet in the dictionary, add a new array.
NSMutableArray* arr = [states objectForKey:state];
if (arr == nil) {
arr = [NSMutableArray array];
[states setObject:arr forKey:state];
}
Then you can insert values into the array, preferably as objects though as Dave DeLong mentions.
You shouldn't be maintaining this data as CSV. That's asking for a world of hurt if you ever need to manipulate this data programmatically (such as what you're trying to do).
You can naïvely break this data up into an array using NSArray * portions = [line componentsSeparatedByString:#","];. Then create a custom object to store each portion (for an example, see this post), and then you can manipulate those objects almost effortlessly.
Naively: (assuming array of strings called strings)
NSMutableDictionary *states = [NSMutableDictionary dictionary];
for (NSString *string in strings) {
NSString *state = [[string componentsSeparatedByString:#", "] lastObject];
NSMutableArray *values = [states objectForKey:state];
if (values == nil) {
values = [NSMutableArray array];
[states setObject:value forKey:state];
}
[values addObject:string];
}
Number of things about this -- first of all, I'm not at my computer, so there is a high chance of typos and or things that I missed. Second, you probably want to adapt the components separated by string line to handle whitespace better.