trying to loop through nested array , obj-c - objective-c

I have an array with 5 sub arrays that I'm trying to loop through,
I can access the first sub array and its objects but when I increase the variable count and try to access the second sub array my program crashes any ideas on a better way to do this? This is the my general method:
-(void) accessArray {
NSArray *myArray; // my array that holds sub arrays
int count = 0; //used to hold which sub array im accesing
NSArray *subArray = [myArray objectAtIndex:count];
//do something with object = [subArray objectAtIndex:0];
//do something with object = [subArray objectAtIndex:1];
}
-(void) otherMethod {
count ++
[self accessArray];
}

for (NSArray *inner in outerArray)
for (id object in inner) {
... do stuff ...
}
}

In addition to bbums answer you can make sub arrays using NSRange (example from NSArray Class Reference):
NSArray *halfArray;
NSRange theRange;
theRange.location = 0;
theRange.length = [wholeArray count] / 2;
halfArray = [wholeArray subarrayWithRange:theRange];
Only saying this because it looks like this might be what you are trying to do. This doesn't iterate through objects, but it is handy when trying to make new arrays from others.

If you REALLY want to do it using your way (i.e. access the subarrays through 'otherMethod'), you'll need to make the 'count' variable accessible to both methods:
int count = 0; // used to hold which sub array I'm accessing
-(void) accessArray {
NSArray *myArray; // my array that holds sub arrays
NSArray *subArray = [myArray objectAtIndex:count];
// do something with object = [subArray objectAtIndex:0];
// do something with object = [subArray objectAtIndex:1];
}
-(void) otherMethod {
count++; // Cannot access count if defined inside 'accessArray'
[self accessArray];
}
Now you can use 'otherMethod' to access the subarrays. But I think the best way to do this is already given in the first answer above by bbum.

Related

NSMutableArray with size of 9

I need to have an NSMutableArray with a constant count of 9 where I can make index-specific insertions and deletions. I know that array = [[NSMutableArray alloc] initWithCapacity:9]; will declare an array with a capacity of 9, but when you get the size of the array, it returns 0.
My first attempt at a solution was to declare an array with capacity 9 (see above) and then fill it with NSNull objects. This code crashes with the error
[NSMutableArray insertObjects:atIndexes:]: array argument is not an NSArray'
- (void) setBlankArray: (NSMutableArray*)array {
for (int i = 0; i < 9; i++) {
[array insertObjects:[NSNull null] atIndexes:i];
}
}
-(void) addCurrentTile: (TileView*)aTile {
[currentTurnTilesArray insertObject:aTile atIndex: aTile.getValue-1];
}
-(void) removeCurrentTile: (TileView*)aTile {
[currentTurnTilesArray removeObjectAtIndex: aTile.getValue-1];
}
Is there a better way to accomplish it?
Not sure what you are trying to accomplish or why, but your removeCurrentTile will break it, because it will reduce the size of the array by 1. What you need to do is wrap this array with a class that guards it such that it can never never never have any other number of elements than 9.
Personally, I think what you're trying to do is silly. If you know you will always have exactly 9 slots, then you should start with a normal array, not a mutable array. It is the objects at each index that you want to mutate - not the array itself. For example, if these things were to be strings, then you would make an immutable array of 9 NSMutableString objects:
NSArray* arr = #[
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string],
[NSMutableString string]
];
Now each string can be mutated into another value, but no strings can be added or removed such as to change the length of the array.
Of course that's just an example (using strings, I mean). For maximum flexibility, this would be an NSArray of nine NSMutableDictionary objects. Now every NSMutableDictionary can contain anything, or nothing. But the number of NSMutableDictionaries will always be exactly nine, because the array itself is immutable.
You're looking for insertObject:atIndex:, or more simply addObject:.
[[NSMutableArray alloc] initWithCapacity:9] does not create an array with 9 elements.
It creates an empty array initialized with enough memory to hold 9 objects.
The purpose of this method is to allocate that much memory at once as you declare, so you can add elements to this array and system has not to allocate memory every time. This is only for optimization.
NSMutableArray reference
I just read your question, and I think I understand exactly what you need. Here is the code:
Declare a property:
#property (nonatomic, retain) NSMutableArray *myArray;
Synthesize it:
#synthesize myArray = _myArray;
Overrride its getter like this:
- (NSMutableArray *)myArray
{
if (!_myArray)
{
_myArray = [[NSMutableArray alloc] initWithCapacity:9];
for (int i = 0; i < 9; i++)
{
[self.myArray addObject:[NSNull null]];
}
}
return _myArray;
}
The "setBlankArray" method will simly set the property to nil, and next time you call the getter of the array property it will get automatically initialized with exactly what you need:
- (void)setBlankArray:(NSMutableArray *)array
{
self.myArray = nil;
}
VERY IMPORTANT: Do not write this code:
for (int i = 0; i < 9; i++)
{
[self.myArray addObject:[NSNull null]];
}
in the method just mentioned as this will make the array to contain 18 elements.
Then write the other 2 methods:
// you can also change the parameter from "id" to "TileView *"
- (void)addCurrentTile:(id)sender
{
NSInteger tileIndex = 1; // replace 1 with ((TileView *) sender).getValue - 1
[self.myArray replaceObjectAtIndex:tileIndex
withObject:sender];
}
// you can also change the parameter from "id" to "TileView *"
- (void)removeCurrentTile:(id)sender
{
NSInteger tileIndex = 1; // replace 1 with ((TileView *) sender).getValue - 1
[self.myArray replaceObjectAtIndex:tileIndex
withObject:[NSNull null]];
}
But, DO NOT FORGET to replace "id" with "TileView *", and TO SET the value of tileIndex to "((TileView *) sender).getValue - 1".
Hope this all makes sense, and is helpful for you.
Best regards

How to recognize the first element in Objective-C style enumeration?

I have an NSMutableArray of NSNumbers, I want to enumerate through all of them with Objective-C styled enumeration. Here's what I've done so far.
for ( NSNumber* number in array )
{
//some code
}
I want to be able to recognize the first object fast, I am able to do this of course,
if ( [array indexOfObject:number] == 0 )
{
//if it's the first object
}
Is there any way to do this faster? There's of course the old-fashioned C style way, and remove the object from array first, and then put it back after enumeration. Just want to know if there's a better technic.
You can try using a method that provides the index of the object currently being enumerated:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (idx == 0) {
// this is the first object
}
}];
Or if you simply want to access the first object of an array:
id obj = [array objectAtIndex:0];
or with the new Objective-C style/syntax:
id obj = array[0];
This solution is faster than accessing and comparing the first array element:
BOOL firstIteration = YES;
for (NSNumber *number in array) {
if (firstIteration) {
// Handle first iteration
firstIteration = NO;
}
// Do something
}
In fast enumeration you cant alter the array. So if you want to remove you have to use old style for(;;) loop.
To find the first object simply use [array objectAtIndex:0]

Count equal objects in NSArray

I've been trying to figure out a way of checking how many of a certain object are in an NSArray.
I've looked through the docs and I'm pretty sure there is no premade method for this. Also I can't find anything here on SO.
Do anybody know about a good way to do this? Because I seriously can't come up with anything.
In this specific case I have an array with strings (most cases several of each) and I want to count how many strings in the array that matches to whatever I ask for.
If this is a primary use of the data structure and order doesn't matter, consider switching to an NSCountedSet which is specifically for solving this problem efficiently.
If you need an ordered collection, and you don't have a huge set of objects, than the fast enumeration answers are the best approach.
If you want to know where the objects are, then use indexesOfObjectsPassingTest:.
If you have a huge number of object, I would look at indexesOfObjectsWithOptions:passingTest: with the NSEnumerationConcurrent option. This will allow you to search the array on multiple cores. (This is only possibly faster on a multi-core device, and even then is probably only faster if you have a very large collection. You should absolutely test before assuming that concurrent will be faster.) Even if you just need the final count, it may be faster for certain data sets to use this method and then use count on the final index set.
There actually is a method for this: - (NSIndexSet *)indexesOfObjectsPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:^(id obj, NSUInteger index, BOOL *stop) {
return [obj isEqualTo:myOtherObject];
}];
Sounds like a case for NSCountedSet, which does what you are after with its initWithArray: initializer:
// Example array of strings
NSArray *array = [NSArray arrayWithObjects:
#"Joe", #"Jane", #"Peter", #"Paul",
#"Joe", #"Peter", #"Paul",
#"Joe",
#"Jane", #"Peter",
nil];
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray: array];
// for-in will let you loop over the counted set
for (NSString *str in countedSet) {
NSLog(#"Count of %#: %ld", str, (long)[countedSet countForObject:str]);
}
One approach would be to iterate and check.
- (int)repeatsOf:(NSString *)repeater inArray:(NSArray *)array {
int count = 0;
for (NSString *item in array) {
if ([item isEqualToString:repeater]) {
count++;
}
}
return count;
}
You could try a simple loop. Suppose needle is your reference string and array is your NSArray of strings:
unsigned int n = 0;
for (NSString * str in array)
{
if ([needle isEqualToString:str])
{
++n;
}
}
Now n holds the count of strings in equal to needle.
You could define a function like this:
- (int)countStringsThatMatch:(NSString*)match inArray:(NSArray*)array
{
int matches = 0;
for (id string in array) {
if ([string isEqualToString:match]) {
matches++;
}
}
return matches;
}
And then use it like:
int count = [self countStringsThatMatch:#"someString" inArray:someArray];
- (NSUInteger) objectCountInArray:(NSArray *)array
matchingString:(NSString *)stringToMatch {
NSUInteger count = 0;
for (NSString *string in array) {
count += [string isEqualToString:stringToMatch] ? 1 : 0;
}
return count;
}
You can try to expand this to use a block that gets an object and returns a BOOL. Then you can use it to compare an array of whatever you want.

Add a tag to NSMutableArray

Is it possible to set a tag for an NSMutableArray? I have to somehow determine, in an array of arrays, the single array which needs to be rewritten, and if I could just set the tag to that inner array to 1 (or some other number), this would be extremely easy.
Example:
NSMutableArray* outerArray = [NSMutableArray new];
NSMutableArray* innerArray1 = [NSMutableArray new];
NSMutableArray* innerArray2 = [NSMutableArray new];
NSMutableArray* innerArray3 = [NSMutableArray new];
NSMutableArray* innerArray4 = [NSMutableArray new];
[outerArray addObject:innerArray1];
[outerArray addObject:innerArray2];
[outerArray addObject:innerArray3];
[outerArray addObject:innerArray4];
//now let's say innerArray1 needs to be rewritten
//I would like to be able to do this
[innerArray1 setTag:100];
//then later, when I need to determine which of the arrays inside outerArray
//needs to be rewritten, I can just do this
for(NSMutableArray* temp in outerArray) {
if(temp.tag == 100) {
//do what I need to do
}
}
But you can't use setTag: with NSMutableArrays. What would be a workaround?
Arrays are ordered collections, so why don't you just keep track of which index needs to be rewritten.
When something happens such that the array at index 0 (which, in your example, would be innerArray1) of outer array needs to be written, cache index 0 -- as a property if this routine needs to span across separate methods.
Then, when it comes time to do the rewrite, consult the cached index. Retrieve the array to be rewritten like this: NSArray *arrayToRewrite = [outerArray objectAtIndex:cachedIndexToRewrite]; Or access it directly: [[outerArray objectAtIndex:cachedIndexToRewrite] replaceObjectAtIndex:whatever withObject:whatever];
You could use an NSMutableDictionary instead. The "tag" would just be the key and the array would be the value.
Use associated objects. You can even add a category to NSMutableArray that would add a tag property to them.
#interface NSMutableArray (TagExtension)
#property (nonatomic, assign) NSInteger tag;
#end
#implementation NSMutableArray (TagExtension)
#dynamic tag;
static char TagExtensionKey;
-(NSInteger)tag {
NSNumber *ourTag = (NSNumber *)objc_getAssociatedObject(self, &TagExtensionKey);
if( ourTag ) {
return( [ourTag integerValue] );
}
return(0);
}
-(void)setTag:(NSInteger)newTag {
objc_setAssociatedObject(self, &TagExtensionKey, [NSNumber numberWithInteger:newTag], OBJC_ASSOCIATION_RETAIN);
}
#end
See also: How to add properties to NSMutableArray via category extension?
Not sure why a dictionary is a bad idea here… as alternatives, you can:
remember the index
or if each entry is a unique array, you can simply refer to it by pointer:
NSArray * tagged = theArray;
for (NSMutableArray * at in outerArray) {
if (tagged == at) {
//do what I need to do
}
}
Make your inner arrays class variables. Then you can just access them as:
for(NSMutableArray* temp in outerArray) {
if(temp == self.innerArray1) {
//do what I need to do
}

NSMutableArray insert object at index

I have an empty mutable array. Is it possible to insert object at index 2 for example, while there's nothing at index 0 and 1? I mean to increase capacity dynamically or something like that. .Regards.
NSMutableArray is not a sparse array; it does not allow empty slots that can be filled in later. initWithCapacity: just hints to the array that it will be filled to a certain amount; it isn't generally necessary in practice and, unless you know exactly how many items you are going to shove in the array, don't bother calling it (just use init).
A mutable array will quite efficiently grow in size as objects are added.
If you need a data structure that supports "holes", then either use something else or put a placeholder object in the slots that are supposed to be empty.
I.e. if you wanted an array with 10 slots, you might do:
NSMutableArray *a = [NSMutableArray array];
for(int i = 0; i<10; i++) [a addObject: [NSNull null]];
You can then check if the retrieved object isEqual: [NSNull null] to know if the slot is empty or not. And you can use replaceObjectAtIndex:withObject: to stick an object at a specific index.
Or you could use a different data structure; a dictionary with the indices as the keys would work, for example.
You can use a NSPointerArray for that.
NSPointerArray is a mutable collection
modeled after NSArray but it can also
hold NULL values, which can be
inserted or extracted (and which
contribute to the object’s count).
Moreover, unlike traditional arrays,
you can set the count of the array
directly.
NSPointerArray is available in OS X v10.5 and later and iOS 6.0 and later. If you target a lower OS version you can, for example:
Use a NSMutableDictionary, wrap you indices into NSNumbers and use these as keys.
Use a NSMutableArray and fill the "holes" with NSNull objects.
Write yourself a SparseArray class using an underlying NSMutableDictionary. Something like this (minimal code, barely tested, but it should give you the idea).
#interface SparseArray : NSObject {
#private
NSMutableDictionary* _dict;
int count;
}
-(SparseArray*)initWithCapacity:(NSUInteger)anInt;
-(id)objectAtIndex:(int)anIndex;
-(void)insertObject:(id)anObject atIndex:(int)anIndex;
- (void)removeObjectAtIndex:(int)anIndex;
-(int)count;
#implementation SparseArray
-(SparseArray*)initWithCapacity:(NSUInteger)anInt {
if ((self = [super init])) {
_dict = [[NSMutableDictionary dictionaryWithCapacity:anInt] retain];
count = 0;
}
return self;
}
-(id)objectAtIndex:(int)anIndex {
NSNumber* key = [NSNumber numberWithInt:anIndex];
id object = [_dict objectForKey:key];
return object;
}
-(void)insertObject:(id)anObject atIndex:(int)anIndex {
NSNumber* key = [NSNumber numberWithInt:anIndex];
[_dict setObject:anObject forKey:key];
count++;
}
- (void)removeObjectAtIndex:(int)anIndex {
NSNumber* key = [NSNumber numberWithInt:anIndex];
id object = [_dict objectForKey:key];
if (object) {
[_dict removeObjectForKey:key];
count--;
}
}
-(int)count {
return count;
}
-(void)dealloc {
[_dict release];
[super dealloc];
}
#end