Equivalente ao NSArray do Mapa

Dado um NSArray de objects NSDictionary (contendo objects e chaves similares) é possível escrever executar um mapa para uma matriz de chave especificada? Por exemplo, em Ruby, isso pode ser feito com:

 array.map(&:name) 

Atualização: se você estiver usando o Swift, consulte o mapa .


O BlocksKit é uma opção:

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

O sublinhado é outra opção. Existe uma function de map , aqui está um exemplo do site:

 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; 

Ele salva apenas algumas linhas, mas eu uso uma categoria no NSArray. Você precisa garantir que seu bloco nunca retorne zero, mas além disso, é uma economia de tempo para casos em que -[NSArray valueForKey:] não 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 

O uso é muito parecido com -[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) 

Eu não tenho idéia do que esse pouco de Ruby faz, mas eu acho que você está procurando pela implementação do NSArray de -valueForKey:. Isso envia -valueForKey: para cada elemento da matriz e retorna uma matriz dos resultados. Se os elementos na matriz de recebimento forem NSDictionaries, -valueForKey: será quase o mesmo que -objectForKey: Ele funcionará enquanto a chave não começar com um @

Para resumir todas as outras respostas:

Ruby (como na pergunta):

 array.map{|o| o.name} 

Obj-C (com valueForKey ):

 [array valueForKey:@"name"]; 

Obj-C (com valueForKeyPath, consulte Operadores de Coleta do KVC ):

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

Obj-C (com enumerateObjectsUsingBlock ):

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

Swift (com mapa , veja closures )

 array.map { $0.name } 

E há algumas bibliotecas que permitem lidar com matrizes de maneira mais funcional. Cocoa Pods é recomendado para instalar outras bibliotecas.

Eu acho que valueForKeyPath é uma boa escolha.

Sente-se abaixo tem exemplos muito legais. Espera que seja útil.

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

Algum exemplo:

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

Eu não sou especialista em Ruby, então eu não estou 100% confiante de que estou respondendo corretamente, mas baseado na interpretação de que ‘map’ faz algo para tudo na matriz e produz uma nova matriz com os resultados, eu acho que você provavelmente quero é algo como:

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

Então você está usando o novo suporte para ‘blocos’ (que são encerramentos em linguagem mais geral) no OS X 10.6 / iOS 4.0 para executar as coisas no bloco em tudo na matriz. Você está escolhendo fazer alguma operação e depois adicionar o resultado a uma matriz separada.

Se você estiver querendo suportar o 10.5 ou o iOS 3.x, provavelmente vai querer colocar o código relevante no object e usar makeObjectsPerformSelector: ou, na pior das hipóteses, fazer uma iteração manual do array usando o 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 

Uma ligeira melhoria em algumas respostas postadas.

  1. Verifica nulo – você pode usar nada para remover objects enquanto está mapeando
  2. O nome do método reflete melhor que o método não modifica a matriz em que é chamado
  3. Isso é mais uma coisa de estilo, mas eu tenho melhorado os nomes de argumentos do bloco IMO
  4. Sintaxe de ponto para contagem

Para o Objective-C, eu adicionaria a biblioteca do ObjectiveSugar a esta lista de respostas: https://github.com/supermarin/ObjectiveSugar

Além disso, seu slogan é “adições do ObjectiveC para humanos. Estilo Ruby”. que deve servir bem ao OP 😉

Meu caso de uso mais comum é mapear um dictionary retornado por uma chamada de servidor para uma matriz de objects mais simples, por exemplo, obtendo um NSArray de NSString IDs de suas postagens NSDictionary:

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

Para Objective-C, eu adicionaria o Higher-Order-Functions a esta lista de respostas: https://github.com/fanpyi/Higher-Order-Functions ;

Existe uma matriz JSON studentJSONList assim:

 [ {"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"]; }]; 

O Swift introduz uma nova function de mapa .

Aqui está um exemplo da documentação :

 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"] 

A function map leva um encerramento que retorna um valor de qualquer tipo e mapeia os valores existentes na matriz para instâncias desse novo tipo.