Ist es bei einem NSArray
der NSDictionary
Objekte (die ähnliche Objekte und Schlüssel enthalten) möglich, eine Zuordnung zu einem Array des angegebenen Schlüssels durchzuführen? In Ruby ist dies beispielsweise möglich mit:
array.map(&:name)
iphone
objective-c
cocoa-touch
cocoa
macos
Stussa
quelle
quelle
map
/collect
method fürNSArray
Antworten:
Update: Wenn Sie Swift verwenden, siehe Karte .
BlocksKit ist eine Option:
NSArray *new = [stringArray bk_map:^id(NSString *obj) { return [obj stringByAppendingString:@".png"]; }];
Unterstrich ist eine weitere Option. Es gibt eine
map
Funktion, hier ein Beispiel von der Website: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;
quelle
mapObjectsUsingBlock:
ist keine Standardfunktion, sondern eine Erweiterung, die von einer anderen Antwort vorgeschlagen wirdEs werden nur ein paar Zeilen gespeichert, aber ich verwende eine Kategorie in NSArray. Sie müssen sicherstellen, dass Ihr Block niemals Null zurückgibt. Ansonsten ist dies eine Zeitersparnis für Fälle, in denen dies
-[NSArray valueForKey:]
nicht funktioniert.@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
Die Verwendung ist ähnlich wie
-[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)
quelle
mapObjectsUsingBlock
. Es stürzt derzeit ab, wenn Sie einen Nullblock übergeben.MutableArray
Rücken zurück. Ist es besser zu tunreturn [result copy]
?NSNull
. Dies ist das Obj-C-Objekt zur Darstellung von Null in Auflistungsklassen.NSNull
ist ziemlich ungewöhnlich und mühsam zu benutzen, da Obj-C kein Unboxing macht, alsoNSNull != nil
. Wenn Sie jedoch nur einige Elemente aus dem Array herausfiltern möchten, können Sie Änderungen vornehmen, ummapObjectsUsingBlock
zu prüfen, ob der Block keine Antworten enthält, und diese überspringen.Ich habe keine Ahnung, was dieses bisschen Ruby macht, aber ich denke, Sie suchen nach NSArrays Implementierung von -valueForKey : . Dies sendet
-valueForKey:
an jedes Element des Arrays und gibt ein Array der Ergebnisse zurück. Wenn die Elemente im empfangenden Array NSDictionaries sind,-valueForKey:
ist dies fast dasselbe wie-objectForKey:
. Es funktioniert, solange der Schlüssel nicht mit einem beginnt@
quelle
valueForKey:
der entsprechende Getter aufgerufen wird. Wenn Sie also im Voraus wissen, welche verschiedenen Aufgaben ein Objekt ausführen soll, können Sie die erforderlichen Getter in das Objekt einfügen (z. B. mit einer Kategorie) und dann NSArrays verwendenvalueForKey:
, um einen Aufruf an einen bestimmten Getter über das Array weiterzuleiten zu jedem Objekt und Abrufen eines Arrays der Ergebnisse.map
Verwenden Sie stattdessen die Kategorie aus Justin Andersons Antwort , wenn Sie wirklich eine Methode benötigen .Um alle anderen Antworten zusammenzufassen:
Ruby (wie in der Frage):
array.map{|o| o.name}
Obj-C (mit
valueForKey
):[array valueForKey:@"name"];
Obj-C (mit
valueForKeyPath
, siehe KVC Collection Operators ):[array valueForKeyPath:@"[collect].name"];
Obj-C (mit
enumerateObjectsUsingBlock
):NSMutableArray *newArray = [NSMutableArray array]; [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [newArray addObject:[obj name]]; }];
Swift (mit Karte , siehe Schließungen )
array.map { $0.name }
Außerdem gibt es einige Bibliotheken, mit denen Sie Arrays funktionaler handhaben können. CocoaPods wird empfohlen, um andere Bibliotheken zu installieren.
quelle
Ich denke, valueForKeyPath ist eine gute Wahl.
Sitzen Sie unten hat sehr coole Beispiele. Hoffe es ist hilfreich.
http://kickingbear.com/blog/archives/9
Ein Beispiel:
NSArray *names = [allEmployees valueForKeyPath: @"[collect].{daysOff<10}.name"]; NSArray *albumCovers = [records valueForKeyPath:@"[collect].{artist like 'Bon Iver'}.<NSUnarchiveFromDataTransformerName>.albumCoverImageData"];
quelle
Ich bin kein Ruby-Experte, daher bin ich nicht zu 100% sicher, dass ich richtig antworte, aber basierend auf der Interpretation, dass 'map' etwas mit allem im Array macht und ein neues Array mit den Ergebnissen erzeugt, denke ich, was Sie wahrscheinlich tun wollen ist so etwas wie:
NSMutableArray *replacementArray = [NSMutableArray array]; [existingArray enumerateObjectsUsingBlock: ^(NSDictionary *dictionary, NSUInteger idx, BOOL *stop) { NewObjectType *newObject = [something created from 'dictionary' somehow]; [replacementArray addObject:newObject]; } ];
Sie verwenden also die neue Unterstützung für 'Blöcke' (die im Allgemeinen Sprachabschlüsse sind) in OS X 10.6 / iOS 4.0, um die Dinge im Block für alles im Array auszuführen. Sie möchten eine Operation ausführen und das Ergebnis dann einem separaten Array hinzufügen.
Wenn Sie 10.5 oder iOS 3.x unterstützen möchten, möchten Sie wahrscheinlich den relevanten Code in das Objekt einfügen und makeObjectsPerformSelector verwenden: oder im schlimmsten Fall eine manuelle Iteration des Arrays mit
for(NSDictionary *dictionary in existingArray)
.quelle
@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
Eine leichte Verbesserung gegenüber einigen der veröffentlichten Antworten.
quelle
Für Objective-C würde ich die ObjectiveSugar-Bibliothek zu dieser Liste von Antworten hinzufügen: https://github.com/supermarin/ObjectiveSugar
Außerdem lautet der Slogan "ObjectiveC-Ergänzungen für Menschen. Ruby-Stil". was gut zu OP passen sollte ;-)
Mein häufigster Anwendungsfall ist das Zuordnen eines Wörterbuchs, das von einem Serveraufruf zurückgegeben wird, zu einem Array einfacherer Objekte, z. B. zum Abrufen eines NSArray von NSString-IDs aus Ihren NSDictionary-Posts:
NSArray *postIds = [results map:^NSString*(NSDictionary* post) { return [post objectForKey:@"post_id"]; }];
quelle
Für Objective-C würde ich die Funktionen höherer Ordnung zu dieser Liste von Antworten hinzufügen: https://github.com/fanpyi/Higher-Order-Functions ;
Es gibt ein JSON-Array studentJSONList wie folgt:
[ {"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<Student *> 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"]; }];
quelle
Hierfür gibt es einen speziellen Schlüsselpfadoperator :
@unionOfObjects
. Wahrscheinlich wurde es[collect]
von früheren Versionen ersetzt.Stellen Sie sich eine
Transaction
Klasse mitpayee
Eigentum vor:NSArray *payees = [self.transactions valueForKeyPath:@"@unionOfObjects.payee"];
Apple-Dokumente zu Array-Operatoren in der Schlüsselwertcodierung .
quelle
Swift führt eine neue Kartenfunktion ein .
Hier ist ein Beispiel aus der Dokumentation :
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"]
Die Zuordnungsfunktion schließt, dass ein Wert eines beliebigen Typs zurückgegeben wird, und ordnet die vorhandenen Werte im Array Instanzen dieses neuen Typs zu.
quelle
[1,2,3].map {$0 * 2} //=> [2,4,6]
. UND, die Frage ist über Obj-C NSArray, nicht Swift;)