NSArray Equivalente de Mapa

Dado un NSArray de objetos NSDictionary (que contienen objetos y claves similares), ¿es posible escribir un mapa en una matriz de clave especificada? Por ejemplo, en Ruby se puede hacer con:

 array.map(&:name) 

Actualización: si estás usando Swift, mira el mapa .


BlocksKit es una opción:

 NSArray *new = [stringArray bk_map:^id(NSString *obj) { return [obj stringByAppendingString:@".png"]; }]; 

Subrayado es otra opción. Hay una función de map , aquí hay un ejemplo del sitio web:

 NSArray *tweets = Underscore.array(results) // Let's make sure that we only operate on NSDictionaries, you never // know with these APIs ;-) .filter(Underscore.isDictionary) // Remove all tweets that are in English .reject(^BOOL (NSDictionary *tweet) { return [tweet[@"iso_language_code"] isEqualToString:@"en"]; }) // Create a simple string representation for every tweet .map(^NSString *(NSDictionary *tweet) { NSString *name = tweet[@"from_user_name"]; NSString *text = tweet[@"text"]; return [NSString stringWithFormat:@"%@: %@", name, text]; }) .unwrap; 

Solo guarda un par de líneas, pero utilizo una categoría en NSArray. -[NSArray valueForKey:] asegurarse de que su bloque nunca devuelva nada, pero aparte de eso, -[NSArray valueForKey:] tiempo en los casos en los que -[NSArray valueForKey:] no funcionará.

 @interface NSArray (Map) - (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block; @end @implementation NSArray (Map) - (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block { NSMutableArray *result = [NSMutableArray arrayWithCapacity:[self count]]; [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [result addObject:block(obj, idx)]; }]; return result; } @end 

El uso es muy parecido a -[NSArray enumerateObjectsWithBlock:] :

 NSArray *people = @[ @{ @"name": @"Bob", @"city": @"Boston" }, @{ @"name": @"Rob", @"city": @"Cambridge" }, @{ @"name": @"Robert", @"city": @"Somerville" } ]; // per the original question NSArray *names = [people mapObjectsUsingBlock:^(id obj, NSUInteger idx) { return obj[@"name"]; }]; // (Bob, Rob, Robert) // you can do just about anything in a block NSArray *fancyNames = [people mapObjectsUsingBlock:^(id obj, NSUInteger idx) { return [NSString stringWithFormat:@"%@ of %@", obj[@"name"], obj[@"city"]]; }]; // (Bob of Boston, Rob of Cambridge, Robert of Somerville) 

No tengo idea de qué es lo que hace Ruby, pero creo que estás buscando la implementación de NSArray de -valueForKey:. Esto envía -valueForKey: a cada elemento de la matriz y devuelve una matriz de los resultados. Si los elementos en la matriz receptora son NSDictionaries, -valueForKey: es casi lo mismo que -objectForKey: Funcionará siempre que la clave no comience con un @

Para resumir todas las demás respuestas:

Ruby (como en la pregunta):

 array.map{|o| o.name} 

Obj-C (con valueForKey ):

 [array valueForKey:@"name"]; 

Obj-C (con valueForKeyPath, ver KVC Collection Operators ):

 [array valueForKeyPath:@"[collect].name"]; 

Obj-C (con enumerateObjectsUsingBlock ):

 NSMutableArray *newArray = [NSMutableArray array]; [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [newArray addObject:[obj name]]; }]; 

Swift (con mapa , ver cierres )

 array.map { $0.name } 

Y, hay un par de bibliotecas que le permiten manejar matrices de una manera más funcional. Se recomienda Cocoa Pods para instalar otras bibliotecas.

Creo que valueForKeyPath es una buena opción.

Siéntate abajo tiene ejemplos geniales. Espera que sea útil.

http://kickingbear.com/blog/archives/9

Un ejemplo:

 NSArray *names = [allEmployees valueForKeyPath: @"[collect].{daysOff<10}.name"]; NSArray *albumCovers = [records valueForKeyPath:@"[collect].{artist like 'Bon Iver'}..albumCoverImageData"]; 

No soy un experto en Ruby, así que no estoy 100% seguro de que estoy respondiendo correctamente, pero basado en la interpretación de que ‘map’ hace algo para todo en la matriz y produce una nueva matriz con los resultados, creo que lo que probablemente querer es algo así como:

 NSMutableArray *replacementArray = [NSMutableArray array]; [existingArray enumerateObjectsUsingBlock: ^(NSDictionary *dictionary, NSUInteger idx, BOOL *stop) { NewObjectType *newObject = [something created from 'dictionary' somehow]; [replacementArray addObject:newObject]; } ]; 

Así que está utilizando el nuevo soporte para ‘bloques’ (que son cierres en lenguaje más general) en OS X 10.6 / iOS 4.0 para realizar las cosas en el bloque de todo en la matriz. Está eligiendo realizar alguna operación y luego agregar el resultado a una matriz separada.

Si está buscando soportar 10.5 o iOS 3.x, probablemente quiera buscar poner el código relevante en el objeto y usar makeObjectsPerformSelector: o, en el peor de los casos, hacer una iteración manual de la matriz usando for(NSDictionary *dictionary in existingArray) .

 @implementation NSArray (BlockRockinBeats) - (NSArray*)mappedWithBlock:(id (^)(id obj, NSUInteger idx))block { NSMutableArray* result = [NSMutableArray arrayWithCapacity:self.count]; [self enumerateObjectsUsingBlock:^(id currentObject, NSUInteger index, BOOL *stop) { id mappedCurrentObject = block(currentObject, index); if (mappedCurrentObject) { [result addObject:mappedCurrentObject]; } }]; return result; } @end 

Una ligera mejora en un par de respuestas publicadas.

  1. Comprueba nil: puede usar nil para eliminar objetos mientras mapea
  2. El nombre del método refleja mejor que el método no modifica la matriz a la que se llama
  3. Esto es más una cuestión de estilo, pero he mejorado IMO los nombres de los argumentos del bloque
  4. Sintaxis de punto para contar

Para Objective-C, agregaría la biblioteca ObjectiveSugar a esta lista de respuestas: https://github.com/supermarin/ObjectiveSugar

Además, su eslogan es “adiciones de ObjectiveC para humanos, estilo Ruby”. que debería adaptarse bien a OP 😉

Mi caso de uso más común es mapear un diccionario devuelto por una llamada de servidor a una matriz de objetos más simples, por ejemplo, obtener un NSArray de identificadores de NSString de tus publicaciones de NSDictionary:

 NSArray *postIds = [results map:^NSString*(NSDictionary* post) { return [post objectForKey:@"post_id"]; }]; 

Para Objective-C, agregaría las funciones de orden superior a esta lista de respuestas: https://github.com/fanpyi/Higher-Order-Functions ;

Hay un JSON array studentJSONList como este:

 [ {"number":"100366","name":"Alice","age":14,"score":80,"gender":"female"}, {"number":"100368","name":"Scarlett","age":15,"score":90,"gender":"female"}, {"number":"100370","name":"Morgan","age":16,"score":69.5,"gender":"male"}, {"number":"100359","name":"Taylor","age":14,"score":86,"gender":"female"}, {"number":"100381","name":"John","age":17,"score":72,"gender":"male"} ] //studentJSONList map to NSArray NSArray *students = [studentJSONList map:^id(id obj) { return [[Student alloc]initWithDictionary:obj]; }]; // use reduce to get average score NSNumber *sum = [students reduce:@0 combine:^id(id accumulator, id item) { Student *std = (Student *)item; return @([accumulator floatValue] + std.score); }]; float averageScore = sum.floatValue/students.count; // use filter to find all student of score greater than 70 NSArray *greaterthan = [students filter:^BOOL(id obj) { Student *std = (Student *)obj; return std.score > 70; }]; //use contains check students whether contain the student named 'Alice' BOOL contains = [students contains:^BOOL(id obj) { Student *std = (Student *)obj; return [std.name isEqual:@"Alice"]; }]; 

Swift introduce una nueva función de mapa .

Aquí hay un ejemplo de la documentación :

 let digitNames = [ 0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine" ] let numbers = [16, 58, 510] let strings = numbers.map { (var number) -> String in var output = "" while number > 0 { output = digitNames[number % 10]! + output number /= 10 } return output } // strings is inferred to be of type String[] // its value is ["OneSix", "FiveEight", "FiveOneZero"] 

La función de mapa toma un cierre que devuelve un valor de cualquier tipo y asigna los valores existentes en la matriz a las instancias de este nuevo tipo.