Create multiple numbered variables based on a int - objective-c

How would I create a number of NSDictionary variables using an array's count?
This is basically what I came up with, but I'm not sure how to make this work with Objective-C syntax. doesntContainAnother is an NSArray. I want the names of the dictionaries to use the current value of loopInt.
int *loopInt = 0;
while (doesntContainAnother.count <= loopInt) {
NSMutableDictionary *[NSString stringWithFormat:#"loopDictionary%i", loopInt] = [[[NSMutableDictionary alloc] init] autorelease];
[NSString stringWithFormat:#"loopDictionary%i", loopInt] = [NSDictionary dictionaryWithObject:[array1 objectAtIndex:loopInt]
forKey:[array2 objectAtIndex:loopInt]];
loopInt = loopInt + 1;
}

Create a mutable array and loop until you reach the original array's count, creating a dictionary and adding it to the mutable array on each iteration.
Your code should look like this.
NSMutableArray *dictionaries = [[NSMutableArray alloc] init];
for (int i = 0; i < doesntContainAnother.count; i++) {
[dictionaries addObject:[NSMutableDictionary dictionaryWithObject:[array1 objectAtIndex:i] forKey:[array2 objectAtIndex:i]]];
}
The approach of creating variables with numbers at the end of their names is an antipattern and not even possible in Objective-C. It's equivalent to an array, but clunkier.

You need to create a mutable array, and then put the objects into the array. You can't create a variable with the same name as the contents of a string, as you have done. For example:
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:[doesntContainAnother count]];
int i = 0; // Note: type is int, not int*
for (i = 0; i < [doesntCountainAnother count]; i++) {
[arr addObject:[NSMutableDictionary dictionary]];
}
// Later...
NSMutableDictionary *d1 = [arr objectAtIndex:3];
Or if you want to pull them out of the list by name:
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:[doesntCountainAnother count]];
int i = 0;
for (i = 0; i < [doesntContainAnother count]; i++) {
[dict setObject:[NSMutableDictionary dictionary] forKey:[NSString stringWithFormat:#"loopDictionary%d", i]];
}
// Later...
NSMutableDictionary *d1 = [dict objectForKey:#"loopDictionary3"];
But the first way is likely the easiest.

Related

NSMutableArray of NSMutableArrays

I want to create an NSMutableArray of NSMutableArrays. Below is the psuedo code:
NSMutableArray *mapcoords = [[NSMutableArray alloc] init];
NSMutableArray *clustercoords = [[NSMutableArray alloc] init];
while (!FINISHED)
{
for(int j = 1;j <= 4;j++)
{
x = arc4random_uniform(45);
[mapcoords addobject:[NSNumber numberWitInt:x]];
}
[clustercoords addobject:mapcoords];
[mapcoords removeAllObjects];
}
When I inspect the "clustercoords" array at the end of the loop, all of the objects contain the same values. I figured this must be because I am pointing to the same object (mapcoords) every time I add it to clustercoords.
I assume that I would need a different several different "mapcoords" arrays so that the objects in mapccords are unique. Is this the correct assumption? Is there another alternative?
Your assumption is correct. Move the declaration and creation of mapcoords inside the while loop.
NSMutableArray *clustercoords = [[NSMutableArray alloc] init];
while (!FINISHED) {
NSMutableArray *mapcoords = [[NSMutableArray alloc] init];
for(int j = 1; j <= 4; j++) {
u_int32_t x = arc4random_uniform(45);
[mapcoords addObject:#(x)];
}
[clustercoords addObject:mapcoords];
}

Selecting array elements using a binary pattern as the selector

I have an NSArray, and I've calculated a list of integers that represent (in binary) the elements I need to pull out of the array into a new array.
For example, I have 7, 11, and 13, whose bit patterns are 000111, 001011, and 001101. I want to grab three arrays, made of elements 0,1,2, then elements 0,1,3, and then 0,2,3 out of the main array.
Construct an NSIndexSet from the bit patterns you have:
#implementation NSIndexSet (NonContiguous)
+ (instancetype)indexSetFromMask:(NSUInteger)mask
{
NSMutableIndexSet * set = [NSMutableIndexSet indexSet];
for( NSUInteger i = 0; i < (sizeof(NSUInteger) * 8); i++ ){
if( mask & (1l << i) ){
[set addIndex:i];
}
}
return set;
}
#end
Then use objectsAtIndexes:
[origArray objectsAtIndexes:[NSIndexSet indexSetFromMask:7]];
// etc.
Assuming you want the output to be in the form [[a,b,c],[a,b,d],[a,c,d]] for the example, you could do something like this:
NSArray *sourceArray = [[NSArray alloc] initWithObjects:#"a",#"b",#"c",#"d",...,nil];
NSArray *grabArray = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:7],[NSNumber numberWithInt:11],[NSNumber numberWithInt:13],...,nil];
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
Then for each element of grabArray, add the correct elements to resultArray:
for (NSNumber num in grabArray) {
int n = [num intValue];
int bit = floor(log2(n)); //get highest bit in the current number
NSMutableArray *curr = [[NSMutableArray alloc] init];
while (n>0) {
if (n>pow(bit,2)) { //if this bit is a 1
[curr addObject:[sourceArray objectAtIndex:bit]];
}
n-=pow(bit,2);
bit-=1;
}
[resultArray addObject:curr];
}
Then resultArray should be the array you are looking for. It will add the objects in reverse order, so if order matters you would want to use [curr insertObject: [sourceArray objectAtIndex:bit] atIndex:0]; instead.

NSMutableDictionary sets objects incorrectly

I have a singleton class where I set up a NSMutableDictionary called completedLevels.
This is how I set it up (in the init method of my singleton):
NSString *mainPath = [[NSBundle mainBundle] bundlePath];
NSString *levelConfigPlistLocation = [mainPath stringByAppendingPathComponent:#"levelconfig.plist"];
NSDictionary *levelConfig = [[NSDictionary alloc] initWithContentsOfFile:levelConfigPlistLocation];
completedLevels = [[NSMutableDictionary alloc]init];
NSMutableDictionary *levelSets = [[NSMutableDictionary alloc]init];
NSMutableDictionary *levels = [[NSMutableDictionary alloc]init];
NSMutableDictionary *stats = [[NSMutableDictionary alloc]init];
[stats setObject:[NSNumber numberWithBool:NO] forKey:#"levelDone"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"stars"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"time"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"bestTime"];
for (int i = 1; i<=18; i++) {
[levels setObject:stats forKey:[NSString stringWithFormat:#"level%d", i]];
}
for(int i= 1; i<=15;i++){
NSString *lvlSet = [NSString stringWithFormat:#"levelSet%d", i];
[levelSets setObject:levels forKey:lvlSet];
}
NSArray *categoriesArray = [levelConfig objectForKey:#"categoriesArray"];
for (int i=0; i<[categoriesArray count]; i++) {
NSString *category = [[levelConfig objectForKey:#"categoriesArray"]objectAtIndex:i];
[completedLevels setObject:levelSets forKey:category];
}
I want to explain my doings:
My intention was to create a dictionary in this form:
category = {
levelSet1 ={
level1 ={
bestTime = 0;
levelDone = 0;
stars = 0;
time = 0;
};
level2={
bestTime = 0;
levelDone = 0;
stars = 0;
time = 0;
};
.
.
.
}
levelSet2 ={
level1 ={
bestTime = 0;
levelDone = 0;
stars = 0;
time = 0;
};
level2={
bestTime = 0;
levelDone = 0;
stars = 0;
time = 0;
};
.
.
.
}
.
.
.
}
%d in the case of levelSet are integers from 1 to 15.
%d in the case of level are integers from 1 to 18.
I have several categories, and thus multiple sets of the example above.
This works well and upon calling NSLog, the dictionary appears in my console as it should.
The problem, however, arises when I want to change some entries in my dictionary as shown in the example below:
NSString *category = [[GameStateSingleton sharedMySingleton]getCurrentCategory];
NSString *levelSet = [NSString stringWithFormat:#"levelSet%d",[[GameStateSingleton sharedMySingleton]getSharedLevelSet]];
NSNumber *currentLevel = [NSNumber numberWithInt:[[GameStateSingleton sharedMySingleton]getSharedLevel]];
NSString *levelString = [NSString stringWithFormat:#"level%d", [currentLevel intValue]];
NSMutableDictionary *categories = [[NSMutableDictionary alloc]initWithDictionary:
[[GameStateSingleton sharedMySingleton]getCompletedLevels]];
[[[[categories objectForKey:category]objectForKey:levelSet]objectForKey:levelString]setObject:[NSNumber numberWithBool:YES] forKey:#"levelDone"];
[[GameStateSingleton sharedMySingleton]setCompletedLevels:categories];
NSLog(#"%#",[[GameStateSingleton sharedMySingleton]getCompletedLevels]);
To explain that:
When the player is done with a level, I want the levelDone entry to change its value. But when I log it afterwards, suddenly all levelDone entries of all categories change to the BOOLEAN value of 1. Why is that ?
------------------- update -----------------------
completedLevels = [[NSMutableDictionary alloc]init];
NSMutableDictionary *levelSets = [[NSMutableDictionary alloc]init];
NSMutableDictionary *levels = [[NSMutableDictionary alloc]init];
NSMutableDictionary *stats = [[NSMutableDictionary alloc]init];
[stats setObject:[NSNumber numberWithBool:NO] forKey:#"levelDone"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"stars"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"time"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"bestTime"];
for (int i = 1; i<=18; i++) {
NSMutableDictionary *statsCopy = [stats mutableCopy];
[levels setObject:statsCopy forKey:[NSString stringWithFormat:#"level%d", i]];
[statsCopy release];
}
for(int i= 1; i<=15;i++){
NSString *lvlSet = [NSString stringWithFormat:#"levelSet%d", i];
NSMutableDictionary *levelsCopy = [levels mutableCopy];
[levelSets setObject:levelsCopy forKey:lvlSet];
[levelsCopy release];
}
NSArray *categoriesArray = [levelConfig objectForKey:#"categoriesArray"];
for (int i=0; i<[categoriesArray count]; i++) {
NSString *category = [[levelConfig objectForKey:#"categoriesArray"]objectAtIndex:i];
NSMutableDictionary *levelSetsCopy = [levelSets mutableCopy];
[completedLevels setObject:levelSetsCopy forKey:category];
[levelSetsCopy release];
}
The part where I retrieve and set it stayed the same...
_________________ SOLUTION ____________________
NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);
I made deep-copies with this method.
Basically, the problem is that you are setting the level dictionary for each level to the same stats object:
for (int i = 1; i<=18; i++) {
[levels setObject:stats forKey:[NSString stringWithFormat:#"level%d", i]];
}
Instead, you need to set them each to a different mutable copy of the object:
for (int i = 1; i<=18; i++) {
NSMutableDictionary *statsCopy = [stats mutableCopy];
[levels setObject:statsCopy forKey:[NSString stringWithFormat:#"level%d", i]];
[statsCopy release];
}
That should solve the first part for you. However, you also have the same problem with the levelSets. Basically, whenever you add a mutable dictionary, you need to determine whether you are trying to point at the same mutable object (you want to keep them synchronized), or whether you want copies of that mutable object (thus, they act independently). And when you look at building this kind of tree structure, that means you need to look at this at every level.
So, looking at the rest of your code, you'll also need to fix your level sets and categories in the same way, by making a mutable copy inside of each loop and then adding that mutable copy instead of adding the original object. And, since you want to mutate the contents of the contents of the dictionaries, every time you make a mutable copy that contains another mutable dictionary, you need to make that copy at each depth.
You are going to have to choose between building these things depth-wise or doing a deep copy. There's another question:
deep mutable copy of a NSMutableDictionary
That covers deep copies of mutable dictionaries pretty well. Alternatively, you can invert the building of the structure so that instead of making copies, you build each dictionary except for stats which you can easily copy.
The deep copy creates copies of each of the mutable entries all the way down to the farthest leaf nodes. If you think about the NSMutableDictionaries as a tree with the base of the tree at the top ( completedLevels in your case) and the leaves as the elements of the stats NSMutableDictionaries that you copy, you want to individually manipulate every element of the tree, whether it is the leaf node or any intermediate.
An illustration could be made as follows, with this representing the contents of MyDictionary:
Top-A:
Second-A-1
Second-A-2
Top-B:
Second-B-1
Second-B-2
To recap, you have the MyDictionary at the top, which has 2 keys (Top-A,Top-B), each of which is associated with a separate NSMutableDictionary object. Each of those NSMutableDictionaries has 2 keys, each associated with a separate object.
When you make a -mutableCopy of MyDictionary, the result is a new NSMutableDictionary with 2 keys (Top-A,Top-B), each associated with an NSMutableDictionary. However, these NSMutableDictionary objects are actually the same objects as the ones in MyDictionary. This is a shallow copy, meaning that there is a new top-level object (the mutable copy of MyDictionary), but that objects associated with each of the keys in the new dictionary are the same objects as were associated with the keys in MyDictionary. As such, if you change Top-A in the copy be associated with a different NSMutableDictionary, then the Top-A in MyDictionary and the copy will no longer be the same. But, if you change the value associated with Second-A-1, it will change both in MyDictionary and the copy of MyDictionary because Top-A points at a single object.
When you make a deep copy, the utility copies every element of every dictionary individually, which means that the copy of MyDictionary will have a separate Top-A, Second-A-1, Second-A-2,Top-B, etc. from the ones that exist in MyDictionary, and therefore, you can change the values of any of the dictionaries (no matter how deep) without fear of changing other objects.

Populating array with integers

Let's say I want to populate NSarray with 50 integers. We know that NSarray accept only objects. So I have to do 50 times
NSNumber *num1 = [NSNumber numberWithInit:10];
NSNumber *num2 = [NSNumber numberWithInit:212];
......
NSNumber *num50 = [NSNumber numberWithInit:12];
Is there more elegant way to achieve that, beacause looks stupid 50 lines of code only for create number objects ?
try this...
NSMutableArray *array=[[NSMutableArray alloc]initWithCapacity:50 ];
for (int i=0; i<0; i++) {
NSNumber *number=[[NSNumber alloc] initWithInt:i];
[array addObject:number];
[number release];
}
//do anything with arrray and release the array later.
is this OK or you are seeking anything else.?
How about using NSMutableArray?
NSMutableArray* arr = [[NSMutableArray alloc] init];
int i = 0;
for(i=0; i<50; i++) {
NSNumber* num = [NSNumber numberWithInt:i]; // use i or random numbers
[arr addObject:num];
}
Your numbers do not seem to follow any particular pattern, so you might be better doing this by creating a C array first:
int myValues[] = { 10, 212, ..., 12 };
NSUInteger count = sizeof(myValues)/sizeof(int); // number of integers in myValues
// abstract the following into a function/method/category if doing more than once
NSMutableArray *objcValues = [NSMutableArray arrayWithCapacity:count];
for(NSUInteger ix = 0; ix < count; ix++)
[objcValues addObject:[NSNumber numberWithInt:myValues[ix]];

Can I assign array size using NSMutableArray?

I used to be a Java Programmer, which the array need to declare the very first time, like this:
int[] anArray; // declares an array of integers
anArray = new int[10]; // allocates memory for 10 integers
I don't know whether the Objective C , NSMutableArray also give me this ability or not. Actually, I want to make a 10*10 array. thz in advance.
I try to do this:
myArray = [[NSMutableArray alloc] initWithCapacity:10];
for (int i=0; i<10; i++) {
myArray[i] = [[NSMutableArray alloc] initWithCapacity:10];
}
But it occurs errors, telling my incompatible type assignment.
The capacity field is seldom useful. The array will be expanded on demand anyway.
And the capacity field just tells the array how much memory you may use. The array's length is still 0.
But you can grow the array from empty:
for (int i = 0; i < 10; ++ i)
[myArray addObject:…];
To read and write to an element in an NSMutableArray, you need:
id x = [array objectAtIndex:i]; // x = array[i];
[array replaceObjectAtIndex:i withObject:y]; // array[i] = y;
You cannot subscript an NSArray directly.
Your code has memory leak. Unlike Java, ObjC doesn't use a GC unless you explicitly enable it (and ObjC on iPhoneOS doesn't have GC). ObjC manages memory by manual reference counting. Basically you need to ensure the ref count of stuff you don't own doesn't change in the process. See http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html for detail.
In your case, [[NSMutableArray alloc] …]; creates an object of ref count +1, then the assignment will take over the array, that means you don't own it any more, but the ref count is not balanced to 0, so this memory will not be properly deallocated. You need to use convenient methods such as [NSMutableArray array…] to create an object with ref count 0.
NSArray's can only store ObjC objects. int in C (ObjC) is a primitive, and cannot be stored into an NSArray. You have to box it into an NSNumber by [NSNumber numberWithInt:0]. You can get back the integer with -intValue.
To conclude, your code needs to be modified as:
-(NSMutableArray*)get10x10Array {
NSMutableArray* arr = [NSMutableArray array];
for (int i = 0; i < 10; ++ i) {
NSMutableArray* subarr = [NSMutableArray array];
for (int j = 0; j < 10; ++ j)
[subarr addObject:[NSNumber numberWithInt:0]];
[arr addObject:subarr];
}
return arr;
}
But ObjC is a superset of C. You can just use a plain 10x10 C array.
int arr[10][10];
You want a 10x10 array -- of what?
myArray = [[NSMutableArray alloc] initWithCapacity:10];
for (int i=0; i<10; i++) {
myArray[i] = [[NSMutableArray alloc] initWithCapacity:10];
}
But it occurs errors, telling my
incompatible type assignment.
Because you can't assign to myArray like that. myArray is an object that represents an array data structure. It is not a C array.
If you want a 10x10 array of a primitive data type, you can declare one like you would in C:
int myArray[10][10];
initWithCapacity: is what you want. It may look like
NSMutableArrat *array = [[NSMutableArray alloc] initWithCapacity:10];
You can't access Cocoa array objects with the bracket notation. Your second bit of code should be:
NSMutableArray *myArray = [[NSmutableArray alloc] initWithCapacity:10];
for (int i = 0; i < 10; i++) {
[myArray insertObject:[NSMutableArray arrayWithCapacity:10] atIndex:i]; // Note: not using myArray[i]!
}
There are two ways to do this.
Plain old C
If you want to store objects, you should use the id type instead of int.
int myarray[10][10];
myarray[5][2] = 412;
Objective-C
NSArray's are not meant to have spaces without objects, if you need them you could use [NSNull null], but if that's the case a C array would be better anyway.
NSMutableArray *myArray = [[NSMutableArray alloc] initWithCapacity:10];
for (int i=0; i < 10; i++) {
NSMutableArray *innerArray = [[NSMutableArray alloc] initWithCapacity:10];
for (int j=0; j < 10; j++) {
[innerArray addObject:[NSNull null]];
}
[myArray addObject:innerArray];
[innerArray release];
}
[[myArray objectAtIndex:5]
replaceObjectAtIndex:2 withObject:[NSNumber numberWithInteger:123]];
NSArray objects have a fixed size that cannot be changed once they have been initialised. NSMutableArray objects can change size. A 10×10 array is sometimes implemented as an NSArray containing 10 individual NSArray objects, each of these containing ten items. This quickly gets cumbersome, sometimes it is easier to resort back to plain C for such a task:
int tenByTen[10][10];
Or, you can use this:
typedef struct
{
int y[10];
} TenInts;
typedef struct
{
TenInts x[10];
} TenByTen;
Then you could do:
- (void) doSomethingWithTenByTen:(const TenByTen) myMatrix
{
NSLog ("%d", myMatrix.x[1].y[5]);
}
And you can also return them from methods:
- (TenByTen) mangleTenByTen:(const TenByTen) input
{
TenByTen result = input;
result.x[1].y[4] = 10000;
return result;
}
You want NSMutableArray +arrayWithCapacity:
Note that setting the initial capacity is merely an optimization - Mutable arrays expand as needed.
EDIT:
To do the 10x10 case,
myArray = [[NSMutableArray alloc] initWithCapacity:10];
for (int i=0; i<10; i++) {
NSMutableArray *subArray = [NSMutableArray arrayWithCapacity:10];
[myArray addObject:subArray];
for (int j = 0; j<10; j++) {
[subArray addObject:[NSNumber numberWithInt:0]];
}
}
Notes:
an array retains the objects added to it, so its not necessary to retain subArray
only objects (not primitive types like "int") can be added to an NSArray, hence the need for NSNumber numberWithInt:
you use methods like objectAtIndex: and replaceObjectAtIndex:withObject: to get/set a value from an NSArray, not array subscript ([]) syntax
See Apple refs for NSArray and NSMutableArray
You can use the following code to resize the NSMutableArray once it was created:
#interface NSMutableArray (Resizing)
- (NSMutableArray *)resize:(NSInteger)newSize;
#end
#implementation NSMutableArray (Resizing)
- (NSMutableArray *)resize:(NSInteger)newSize
{
int size = (newSize > [self count]) ? self.count : newSize;
NSMutableArray *array = [NSMutableArray arrayWithCapacity:size];
for (int i = 0; i < size; i++){
[array addObject:[self objectAtIndex:i]];
}
return array;
}
#end