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)
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)
map
/ collect
method für NSArray
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;
mapObjectsUsingBlock:
ist keine Standardfunktion, sondern eine Erweiterung, die von einer anderen Antwort vorgeschlagen wird
Es 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)
mapObjectsUsingBlock
. Es stürzt derzeit ab, wenn Sie einen Nullblock übergeben.
MutableArray
Rücken zurück. Ist es besser zu tun return [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, also NSNull != nil
. Wenn Sie jedoch nur einige Elemente aus dem Array herausfiltern möchten, können Sie Änderungen vornehmen, um mapObjectsUsingBlock
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@
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 verwenden valueForKey:
, 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.
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"];
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)
.
@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
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"];
}];
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"];
}];
Hierfür gibt es einen speziellen Schlüsselpfadoperator : @unionOfObjects
. Wahrscheinlich wurde es [collect]
von früheren Versionen ersetzt.
Stellen Sie sich eine Transaction
Klasse mit payee
Eigentum vor:
NSArray *payees = [self.transactions valueForKeyPath:@"@unionOfObjects.payee"];
Apple-Dokumente zu Array-Operatoren in der Schlüsselwertcodierung .
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.
[1,2,3].map {$0 * 2} //=> [2,4,6]
. UND, die Frage ist über Obj-C NSArray, nicht Swift;)