2013-06-14 17 views
13

Dire che ho una serie come questa:Come posso più facilmente appiattire un array tridimensionale in Cocoa?

NSArray *threeDimensionalArray = @[ 
@[  
    @[ @"Peter", @"Paul", @"Mary" ], @[ @"Joe", @"Jane" ] 
    ], 
@[ 
    @[ @"Alice", @"Bob" ] 
    ] 
]; 

e voglio che diventi:

@[ @"Peter", @"Paul", @"Mary", @"Joe", @"Jane", @"Alice", @"Bob" ] 

Come posso più facilmente creare questo array appiattita?

+0

+1 sia per la domanda che per la risposta, soluzione elegante. –

risposta

26

L'operatore di raccolta codice KVC (Key-Value Coding) @unionOfArrays appiattisce un livello di array, quindi applicarlo due volte produce il risultato desiderato.

Gli operatori di raccolta (diversi da @count) necessitano di un percorso chiave per una proprietà di raccolta e poiché i nostri oggetti sono già matrici (e quindi raccolte) in se stesse, il percorso chiave dovrà essere self.

Abbiamo quindi bisogno di applicare @unionOfArrays due volte con il percorso della chiave self, ottenendo la seguente chiamata KVC per appiattire una matrice 3D:

NSArray *flattenedArray = [threeDimensionalArray valueForKeyPath: @"@[email protected]"]; 
1

Mi rendo conto che questa discussione è un po 'vecchio, ma avevo bisogno di una soluzione dove il numero di livelli in profondità non conta davvero. I seguenti metodi possono essere aggiunti a una categoria su NSArray. Ho anche incluso il test per questi metodi:

// This is the method that would be used from an outside class 
- (NSArray *)flatten { 
    NSArray *array = self; 
    while (![array isFlattened]) { 
     array = [array flattenOneLevel]; 
    } 
    return [NSArray arrayWithArray:array]; 
} 

- (NSArray *)flattenOneLevel { 
    NSMutableArray *array = [NSMutableArray array]; 
    for (id object in self) { 
     [object isKindOfClass:self.class] ? [array addObjectsFromArray:object] : [array addObject:object]; 
    } 
    return array; 
} 

- (BOOL)isFlattened { 
    BOOL flattened = YES; 
    for (id object in self) { 
     if ([object isKindOfClass:self.class]) { 
      flattened = NO; 
      break; 
     } 
    } 
    return flattened; 
} 

Ecco il test per questi metodi per assicurarsi che funzioni correttamente:

it(@"should flatten an array", ^{ 
    NSArray *initialArray = @[@[@23, @354, @1, @[@7], @[@[@3]]], @[@[@890], @2, @[@[@6], @8]]]; 
    NSArray *expectedArray = @[@23, @354, @1, @7, @3, @890, @2, @6, @8]; 
    expect([initialArray flatten]).equal(expectedArray); 
}); 
0

una risposta alternativa utilizzando la ricorsione, che avrà più memoria (su lo stack), ma per coloro che amano la ricorsione, è più semplice da leggere:

- (NSArray *) flatten; 
{ 
    NSMutableArray *flattedArray = [NSMutableArray new]; 

    for (id item in self) { 
     if ([[item class] isSubclassOfClass:[NSArray class]]) { 
      [flattedArray addObjectsFromArray:[item flatten]]; 
     } else { 
      [flattedArray addObject:item]; 
     } 
    } 

    return flattedArray; 
} 

e l'estensione delle prove:

+ (void) unitTests; 
{ 
    NSArray *flattenedArray; 

    NSArray *initialArray1 = @[@[@23, @354, @1, @[@7], @[@[@3]]], @[@[@890], @2, @[@[@6], @8]]]; 
    NSArray *expectedArray1 = @[@23, @354, @1, @7, @3, @890, @2, @6, @8]; 
    flattenedArray = [initialArray1 flatten]; 
    SPASLogDetail(@"flattenedArray: %@", flattenedArray); 
    AssertIf(![flattenedArray isEqualToArray:expectedArray1], @"Arrays are not equal"); 

    NSArray *initialArray2 = @[@[@23, @354, @1, [@[@7] mutableCopy], @[@[@3]]], @[[@[@890] mutableCopy], @2, @[@[@6], @8]]]; 
    NSArray *expectedArray2 = expectedArray1; 
    flattenedArray = [initialArray2 flatten]; 
    SPASLogDetail(@"flattenedArray: %@", flattenedArray); 
    AssertIf(![flattenedArray isEqualToArray:expectedArray2], @"Arrays are not equal"); 
} 
Problemi correlati