wake-up-neo.com

NSArray-Äquivalent der Karte

Ist eine NSArray von NSDictionary-Objekten (die ähnliche Objekte und Schlüssel enthalten) möglich? In Ruby kann beispielsweise Folgendes ausgeführt werden:

array.map(&:name)
69
Stussa

Update: Wenn Sie Swift verwenden, siehe map .


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;
18
Senseful

Es spart nur ein paar Zeilen, aber ich benutze eine Kategorie für NSArray. Sie müssen sicherstellen, dass Ihr Block niemals Null zurückgibt, aber ansonsten ist es eine Zeitersparnis für Fälle, in denen -[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 ähnelt -[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)
122
Justin Anderson

Ich habe keine Ahnung, was dieses bisschen von Ruby bewirkt, aber ich denke , dass Sie nach der Implementierung von - durch NSArray suchen. 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, entspricht -valueForKey: fast -objectForKey:. Es funktioniert, solange der Schlüssel nicht mit einem @ beginnt.

74
JeremyP

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-Auflistungsoperatoren ):

[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 map , siehe Verschlüsse )

array.map { $0.name }

Außerdem gibt es einige Bibliotheken, mit denen Sie Arrays funktionaler behandeln können. Cocoa Pods wird zur Installation anderer Bibliotheken empfohlen.

31
tothemario

Ich denke, valueForKeyPath ist eine gute Wahl.

Sit unten hat sehr coole Beispiele. Hoffentlich ist es hilfreich.

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

Einige Beispiele:

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

Ich bin kein Ruby-Experte, also bin ich nicht zu 100% sicher, dass ich richtig antworte, aber basierend auf der Interpretation, dass "map" etwas zu allem im Array macht und ein neues Array mit den Ergebnissen erzeugt, denke ich, was Sie wahrscheinlich denken 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];
    }
];

In OS X 10.6/iOS 4.0 verwenden Sie also die neue Unterstützung für "Blöcke" (die im allgemeinen Sprachgebrauch Abschlüsse sind), um das gesamte Block im gesamten Array auszuführen. Sie entscheiden sich für eine Operation und fügen das Ergebnis dann einem separaten Array hinzu.

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 schlechtesten Fall eine manuelle Iteration des Arrays mithilfe von for(NSDictionary *dictionary in existingArray) durchführen.

4
Tommy
@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 kleine Verbesserung gegenüber einigen Antworten.

  1. Überprüft auf Null: Sie können mit nil Objekte entfernen, während Sie eine Zuordnung vornehmen
  2. Der Methodenname zeigt besser, dass die Methode das aufgerufene Array nicht ändert
  3. Dies ist mehr eine Stilrichtung, aber ich habe IMO die Argumentnamen des Blocks verbessert
  4. Punktsyntax für die Zählung
2
james_womack

Für Objective-C würde ich die ObjectiveSugar-Bibliothek zu dieser Antwortliste hinzufügen: https://github.com/supermarin/ObjectiveSugar

Außerdem lautet der Slogan "ObjectiveC-Ergänzungen für Menschen. Ruby-Stil". was sollte OP gut passen ;-)

Mein häufigster Anwendungsfall ist das Zuordnen eines Wörterbuchs, das von einem Serveraufruf zurückgegeben wird, zu einem Array von einfacheren Objekten, z. Ein NSArray von NSString-IDs aus Ihren NSDictionary-Posts abrufen:

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

Für Objective-C würde ich die Liste der Antworten mit höherer Ordnung 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"];
}];
0
范陆离