How to create a jagged array in Objective-C

I don’t know if you ever ran into that kind of problem before, where you need to initialize a jagged array with Objective-C, but I did. I was disappointed to find out that Objective-C doesn’t have any easy way of recursively call a variadic method. I read several posts talking about using NSInvocation to achieve this, but as Apple stated, it’s not capable of doing so. Setting an argument on the NSInvocation object that is beyond the static arguments returned by the method signature will result in an out of bounds exception.

I found a way, it’s not super pretty, but it works just fine. Here’s a few examples of how simple this task is using other languages:

PHP:

<?php
    function createJaggedArray($length) {
        $args = func_get_args();
        array_shift($args); // We don't need the first parameter, it's already defined.
        
        $arr = array();
        while ($length--) {
            $arr[$length] = count($args) ? call_user_func_array('createJaggedArray', $args) : 0;
        }
        return $arr;
    }

    $jaggedArray = createJaggedArray(3, 3, 4, 4); // creates var[3][3][4][4]
    $jaggedArray2 = createJaggedArray(10, 3, 9); // creates var[10][3][9]

?>

ActionScript 3:

package {
    public class ArrayUtil {
        public static function createJaggedArray(len:int, ...args):Array {
            var arr:Array = new Array(len);
            
            while(len--) {
                arr[len] = args.length ? createJaggedArray.apply(null, args): 0;
            }

            return arr;
        }
    }
}

var myArray:Array = createJaggedArray(3, 3, 4, 4); // creates var[3][3][4][4]
var myArray2:Array = createJaggedArray(10, 3, 9); // creates var[10][3][9]

Here’s what I came up with for Objective-C. As you can tell, it’s quite convoluted. I’m sure there’s a better way of executing this, but at least, if you need something right away, that might answer *some* questions:

CreateJaggedLib.h:

@interface CreateJaggedArray : NSObject {
}

- (NSArray *)createJaggedArray:(NSNumber *)len, ...;
- (NSArray *)createJaggedArrayFromArray:(NSNumber *)len childrenLengths:(NSArray *)childrenLengths;

@end

CreateJaggedArray.m:

#import "CreateJaggedArray.h"

@implementation CreateJaggedArray

- (NSArray *)createJaggedArray:(NSNumber *)len, ... {
    NSMutableArray *argArray = [[NSMutableArray alloc] init];
    NSMutableArray *buildingArray = [[NSMutableArray alloc] init];

    NSNumber *eachArgument;
    if (len) {
        va_list argumentList;
        va_start(argumentList, len);
        while (eachArgument = va_arg(argumentList, NSNumber *) {
            [argArray addObject:eachArgument];
        }
        va_end(argumentList);
    }

    NSInteger length = [len intValue];

    while (length--) {
        NSMutableArray *nextArray = [[NSMutableArray alloc] init];
        for (NSInteger i = 1; i < [argArray count]; i++) {
            [nextArray addObject:[argArray objectAtIndex:i]];
        }
        
        NSNumber *value = [NSNumber numberWithInt:[[argArray objectAtIndex:0] intValue]];
        NSArray *resultArray = [self createJaggedArrayFromArray:value childrenLengths:nextArray];
        [buildingArray addObject:resultArray];
    }
    return buildingArray;
}

- (NSArray *)createJaggedArrayFromArray:(NSNumber *)len childrenLengths:(NSArray *)childrenLengths {
    NSInteger length = [len intValue];
    NSMutableArray *buildingArray = [[NSMutableArray alloc] init];

    while (length--) {
        if ([childrenLengths count] > 1) {
            NSMutableArray *nextArray = [[NSMutableArray alloc] initWithCapacity:([childrenLengths count] - 1)];
            for (NSInteger i = 1; i < [childrenLengths count]; i++) {
                [nextArray addObject:[childrenLengths objectAtIndex:i]];
            }
            NSNumber *value = [NSNumber numberWithInt:[[childrenLengths objectAtIndex:0] intValue];
            NSArray *resultArray = [self createJaggedArrayFromArray:value childrenLengths:nextArray];
            [buildingArray addObject:resultArray];
        }
        else {
            NSMutableArray *valueArray = [[NSMutableArray alloc] init];
            for (NSInteger i = 0; i < [[childrenLengths objectAtIndex:0] intValue]; i++) {
                [valueArray insertObject:[NSNumber numberWithInt:0] atIndex:i];
            }
            [buildingArray addObject:valueArray];
        }
    }
    return buildingArray;
}

@end

Call:

NSArray *jaggedArray = [<delegate> createJaggedArray:[NSNumber numberWithInt:3], [NSNumber numberWithInt:3], [NSNumber numberWithInt:4], [NSNumber numberWithInt:4], nil]; // Creates var[3][3][4][4]
NSArray *jaggedArray2 = [<delegate> createJaggedArray:[NSNumber numberWithInt:10], [NSNumber numberWithInt:3], [NSNumber numberWithInt:9], nil]; // Creates var[10][3][9]

As you can see, this can get pretty convoluted. I had to create a method that reacts differently for all the children parameters, since the recursive call of a variadic method isn’t supported with Objective-C. You could simply call the createJaggedArrayFromArray method using an array of lengths instead of using the nil-terminated list of arguments, but sometimes, it makes it easier for porting code from one language to another, to make sure it works the same way.

If someone has a better idea on how to fix this, please share! 🙂

One Comment

  1. Vicky says:

    Totally agree with WRT MonoTouch Josh. Not only will learning Objective-C help you with inreotp, there are so many samples out there (not to mention all the SDK docs and tutorials from Apple) that it’s almost a necessity. MonoTouch is great, I’ve used it several times now for building iOS applications, but at some point you run into a snag that requires some research and invariably the solution exists in some ObjC sample Plus, it gives you a better appreciation for MonoTouch’s brevity consider the pinnacle example of trimming a string in ObjC:myString = [myString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];vs:myString = myString.Trim(new char[] { , \r’, \n’ });

Leave a Reply

*