web-dev-qa-db-fra.com

Tableaux 2D utilisant NSMutableArray

J'ai besoin de créer un tableau bidimensionnel modifiable en Objective-C.

Par exemple j'ai:

NSMutableArray *sections;
NSMutableArray *rows;

Chaque élément de sections consiste en un tableau rows. rows est un tableau qui contient des objets.

Et je veux faire quelque chose comme ça:

[ sections[i] addObject: objectToAdd]; //I want to add a new row

Pour avoir quelque chose comme ceci: Section 0, lignes: obj1, obj2, obj3 Section 1, lignes: obj4, obj5, obj6, obj 7 ...

Y a-t-il un moyen de faire cela en Objective-C?

43

Tout d'abord, vous devez allouer et initialiser vos objets avant utilisation, quelque chose comme: NSMutableArray * sections = [[NSMutableArray alloc] initWithCapacity:10]; Pour les lignes, vous avez besoin d'un objet pour chaque objet, pas un seul NSMutableArray * rows;.

Deuxièmement, selon que vous utilisiez ou non Xcode 4.4+ (qui introduisait l’indice, a.k.a section[i] et section[i] = …), vous devrez peut-être utiliser [sections objectAtIndex:i] pour lire et [section replaceObjectAtIndex:i withObject: objectToAdd] pour écrire.

Troisièmement, un tableau ne peut pas avoir de trous, c’est-à-dire obj1, nil, obj2. Vous devez fournir l'objet réel à chaque index. Si vous n'avez rien à mettre, vous pouvez utiliser NSNull object.

De plus, n'oubliez pas que vous pouvez également stocker des objets Objective-C dans des tableaux en plain C:

id table[lnum][rnum];
table[i][j] = myObj;
88
mouviciel

Si vous souhaitez utiliser des tableaux, vous pouvez initialiser votre tableau sections, puis ajouter un tableau de lignes comme suit:

NSMutableArray *sections = [[NSMutableArray alloc] init];
NSMutableArray *rows = [[NSMutableArray alloc] init];
//Add row objects here

//Add your rows array to the sections array
[sections addObject:rows];

Si vous souhaitez ajouter cet objet de lignes à un certain index, utilisez ce qui suit:

//Insert your rows array at index i
[sections insertObject:rows atIndex:i];

Vous pouvez ensuite modifier ce tableau de lignes en récupérant le tableau:

//Retrieve pointer to rows array at index i
NSMutableArray *rows = [sections objectAtIndex:i]
//modify rows array here

Vous pouvez toujours créer votre propre classe appelée Section, qui a un membre NSMutableArray appelé rows; puis vous stockez vos lignes dans ce tableau et les objets Section dans un tableau:

@interface Section : NSObject {
    NSMutableArray *rows;
}

Ensuite, vous créez simplement des éléments Section et vous pouvez créer des méthodes au sein de votre classe pour ajouter/supprimer des éléments de ligne. Ensuite, vous empaquetez tous les éléments Sections dans un autre tableau:

Section *aSection = [[Section alloc] init];
//add any rows to your Section instance here

NSMutableArray *sections = [[NSMutableArray alloc] init];
[sections addObject:aSection];

Cela devient plus utile si vous souhaitez ajouter plus de propriétés pour chaque instance Section.

17
Alex Rozanski

Le langage ne prend pas en charge les tableaux multidimensionnels orientés objet, mais vous pouvez créer une classe qui le fait à l'aide d'un NSMutableArray complet de NSMutableArrays, comme ci-dessous. Je n'ai pas essayé de compiler ceci ou quoi que ce soit, je l'ai simplement tapé.

@interface SectionArray : NSObject {
  NSMutableArray *sections;
}
- initWithSections:(NSUInteger)s rows:(NSUInteger)r;
+ sectionArrayWithSections:(NSUInteger)s rows:(NSUInteger)r;
- objectInSection:(NSUInteger)s row:(NSUInteger)r;
- (void)setObject:o inSection:(NSUInteger)s row:(NSUInteger)r;
@end
@implementation SectionArray
- initWithSections:(NSUInteger)s rows:(NSUInteger)r {
  if ((self = [self init])) {
    sections = [[NSMutableArray alloc] initWithCapacity:s];
    NSUInteger i;
    for (i=0; i<s; i++) {
      NSMutableArray *a = [NSMutableArray arrayWithCapacity:r];
      for (j=0; j<r; j++) {
        [a setObject:[NSNull null] atIndex:j];
      }
      [sections addObject:a];
    }
  }
  return self;
}
+ sectionArrayWithSections:(NSUInteger)s rows:(NSUInteger)r {
  return [[[self alloc] initWithSections:s rows:r] autorelease];
}
- objectInSection:(NSUInteger)s row:(NSUInteger)r {
  return [[sections objectAtIndex:s] objectAtIndex:r];
}
- (void)setObject:o inSection:(NSUInteger)s row:(NSUInteger)r {
  [[sections objectAtIndex:s] replaceObjectAtIndex:r withObject:0];
}
@end

Vous l'utiliseriez comme ceci:

SectionArray *a = [SectionArray arrayWithSections:10 rows:5];
[a setObject:@"something" inSection:4 row:3];
id sameOldSomething = [s objectInSection:4 row:3];
9
Jack Nutting

que se passe-t-il. Tant que nous relancerons cette question, voici quelque chose à l’âge de la syntaxe de collection littérale et de l’interprétation des formats visuels!

Au cas où quelqu'un se le demanderait, cela fonctionne:

NSMutableArray *multi = [@[ [@[] mutableCopy] , [@[] mutableCopy] ] mutableCopy];
multi[1][0] = @"Hi ";
multi[1][1] = @"There ";
multi[0][0] = @"Oh ";
multi[0][1] = @"James!";        
NSLog(@"%@%@%@%@", multi[0][0], multi[1][0], multi[1][1], multi[0][1]);

Résultat: "Oh, salut James!"

Bien sûr, il y a le problème d'essayer quelque chose comme multi[3][5] = @"?" et d'obtenir une exception d'index invalide, alors j'ai écrit une catégorie pour NSMutableArray.

@interface NSMutableArray (NullInit)
+(NSMutableArray *)mutableNullArrayWithSize:(NSUInteger)size;
+(NSMutableArray *)mutableNullArraysWithVisualFormat:(NSString *)string;
@end

@implementation NSMutableArray (NullInit)

+(NSMutableArray *)mutableNullArrayWithSize:(NSUInteger)size
{
    NSMutableArray *returnArray = [[NSMutableArray alloc] initWithCapacity:size];
    for (int i = 0; i < size; i++) {
        [returnArray addObject:[NSNull null]];
    }
    return returnArray;
}

+(NSMutableArray *)mutableNullArraysWithVisualFormat:(NSString *)string
{
    NSMutableArray *returnArray = nil;
    NSRange bitRange;
    if ((bitRange = [string rangeOfString:@"^\\[\\d+]" options:NSRegularExpressionSearch]).location != NSNotFound) {
        NSUInteger size = [[string substringWithRange:NSMakeRange(1, bitRange.length - 2)] integerValue];
        if (string.length == bitRange.length) {
            returnArray = [self mutableNullArrayWithSize:size];
        } else {
            returnArray = [[NSMutableArray alloc] initWithCapacity:size];
            NSString *nextLevel = [string substringWithRange:NSMakeRange(bitRange.length, string.length - bitRange.length)];
            NSMutableArray *subArray;
            for (int i = 0; i < size; i++) {
                subArray = [self mutableNullArraysWithVisualFormat:nextLevel];
                if (subArray) {
                    [returnArray addObject:subArray];
                } else {
                    return nil;
                }
            }
        }
    } else {
        return nil;
    }
    return returnArray;
}

@end

Comme vous pouvez le constater, nous avons une méthode pratique pour créer un tableau complet de NSNull afin que vous puissiez définir des index avec wild abandon.

Deuxièmement, il existe une méthode récursive qui analyse une chaîne avec un format visuel tel que: [3][12] (tableau 3 x 12). Si votre chaîne n'est pas valide, la méthode retourne nil, mais si elle est valide, vous obtenez un tableau multidimensionnel complet de la taille que vous spécifiez.

Voici quelques exemples:

NSMutableArray *shrub = [NSMutableArray mutableNullArrayWithSize:5];
NSMutableArray *tree = [NSMutableArray mutableNullArraysWithVisualFormat:@"[3][12]"]; // 2-Dimensional Array
NSMutableArray *threeDeeTree = [NSMutableArray mutableNullArraysWithVisualFormat:@"[3][5][6]"]; // 3-Dimensional Array
NSMutableArray *stuntedTree = [NSMutableArray mutableNullArraysWithVisualFormat:@"[6][4][k]"]; // Returns nil

Vous pouvez passer autant de dimensions que vous le souhaitez dans la méthode de format visuel, puis y accéder avec la syntaxe littérale, comme suit:

NSMutableArray *deepTree = [NSMutableArray mutableNullArraysWithVisualFormat:@"[5][3][4][2][7]"];
deepTree[3][2][1][0][5] = @"Leaf";
NSLog(@"Look what's at 3.2.1.0.5: %@", deepTree[3][2][1][0][5]);

Quoi qu'il en soit, cela a-t-il été fait plus comme un exercice qu'autre chose; c'est probablement assez efficace dans le grand schéma des choses ... considérant comment nous faisons des tableaux multidimensionnels de pointeurs d'objet Objective-C.

6
Matt Mc

Merci à Jack pour son code, j'ai un peu travaillé dessus; j'ai besoin d'un nsmutablearray multidimensionnel pour les chaînes dans l'un de mes projets, il a encore certaines choses à réparer mais fonctionne uniquement pour le moment, je vais le poster ici, je suis ouvert aux suggestions s'il vous plaît car je ne suis qu'un débutant en objectif C en ce moment ...

@interface SectionArray : NSObject {

  NSMutableArray *sections;    

}
- initWithSections:(NSUInteger)intSections:(NSUInteger)intRow;
+ sectionArrayWithSections:(NSUInteger)intSections:(NSUInteger)intRows;
- objectInSection:(NSUInteger)intSection:(NSUInteger)intRow;
- (void)setObject:(NSString *)object:(NSUInteger)intSection:(NSUInteger)intRow;
@end

@implementation SectionArray

- initWithSections:(NSUInteger)intSections:(NSUInteger)intRow {
    NSUInteger i;
    NSUInteger j;

    if ((self = [self init])) {
        sections = [[NSMutableArray alloc] initWithCapacity:intSections];
        for (i=0; i < intSections; i++) {
            NSMutableArray *a = [NSMutableArray arrayWithCapacity:intRow];
            for (j=0; j < intRow; j++) {
                [a insertObject:[NSNull null] atIndex:j];
            }
            [sections addObject:a];
        }
    }
    return self;    
}
- (void)setObject:(NSString *)object:(NSUInteger)intSection:(NSUInteger)intRow {
    [[sections objectAtIndex:intSection] replaceObjectAtIndex:intRow withObject:object];
}
- objectInSection:(NSUInteger)intSection:(NSUInteger)intRow {
    return [[sections objectAtIndex:intSection] objectAtIndex:intRow];
}
+ sectionArrayWithSections:(NSUInteger)intSections:(NSUInteger)intRows {
    return [[self alloc] initWithSections:intSections:intRows] ;
}
@end

Cela fonctionne bien !!!

je l'utilise actuellement comme ça

SectionArray *secA = [[SectionArray alloc] initWithSections:2:2];
[secA setObject:@"Object":0:0];
[secA setObject:@"ObjectTwo":0:1];
[secA setObject:@"ObjectThree":1:0];
[secA setObject:@"ObjectFour":1:1];

NSString *str = [secA objectInSection:1:1];

NSLog(@" object is = %@" , str);

Merci encore Jack !!

5
Jericho

Rétablir un ancien thread, mais j'ai retravaillé le code de Jack afin qu'il 1. compile et 2. qu'il soit dans l'ordre des tableaux 2D c [lignes] [colonnes] au lieu de [sections (colonnes)] [lignes] comme il l'a. Voici!

TwoDArray.h:

#import <Foundation/Foundation.h>

@interface TwoDArray : NSObject

@property NSMutableArray *rows;

- initWithRows:(NSUInteger)rows columns:(NSUInteger)columns;
+ sectionArrayWithRows:(NSUInteger)rows columns:(NSUInteger)columns;
- objectInRow:(NSUInteger)row column:(NSUInteger)column;
- (void)setObject:(id)obj inRow:(NSUInteger)row column:(NSUInteger)column;

@end

TwoDArray.m:

#import "TwoDArray.h"

@implementation TwoDArray

- (id)initWithRows:(NSUInteger)rows columns:(NSUInteger)columns {
    if ((self = [self init])) {
        self.rows = [[NSMutableArray alloc] initWithCapacity: rows];
        for (int i = 0; i < rows; i++) {
            NSMutableArray *column = [NSMutableArray arrayWithCapacity:columns];
            for (int j = 0; j < columns; j++) {
                [column setObject:[NSNull null] atIndexedSubscript:j];
            }
            [self.rows addObject:column];
        }
    }
    return self;
}
+ (id)sectionArrayWithRows:(NSUInteger)rows columns:(NSUInteger)columns {
    return [[self alloc] initWithRows:rows columns:columns];
}
- (id)objectInRow:(NSUInteger)row column:(NSUInteger)column {
    return [[self.rows objectAtIndex:row] objectAtIndex:column];
}
- (void)setObject:(id)obj inRow:(NSUInteger)row column:(NSUInteger)column {
    [[self.rows objectAtIndex:row] replaceObjectAtIndex:column withObject:obj];
}
@end
0
Matt Cooper

FYI: Le code de Jack a besoin de beaucoup de travail avant de fonctionner. Entre autres problèmes, autorelease peut provoquer la libération des données avant que vous n'y accédiez et son utilisation appelle la méthode de classe arrayWithSections: rows: lorsqu'elle est réellement définie en tant que sectionArrayWithSections: rows:

J'essaierai peut-être de poster le code de travail plus tard si j'en ai l'occasion.

0
dejo