it-swarm.dev

NSArray Harita Eşdeğeri

Göz önüne alındığında NSArray nesnelerinin NSDictionary (benzer nesneler ve anahtarlar içeren) vermesi, belirtilen bir anahtar dizisine eşleme yapmak için yazmak mümkündür. Örneğin, Ruby'de şu şekilde yapılabilir:

array.map(&:name)
69
Stussa

Güncelleme: Swift kullanıyorsanız, bakınız harita .


BlocksKit bir seçenektir:

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

Alt çizgi başka bir seçenektir. map işlevi var, işte web sitesinden bir örnek:

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

Yalnızca birkaç satır kaydeder, ancak NSArray'de bir kategori kullanırım. Bloğunuzun asla sıfır döndürmemesini sağlamalısınız, ancak bunun dışında -[NSArray valueForKey:] 'nin çalışmadığı durumlar için zaman tasarrufu sağlar.

@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

Kullanımı -[NSArray enumerateObjectsWithBlock:] gibidir:

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

Ruby'nin bu bitinin ne yaptığı hakkında hiçbir fikrim yok, ancak düşünüyorum NSArray'ın -valueForKey: uygulamasını uyguladığını düşünüyorum. Bu, dizinin her öğesine -valueForKey: gönderir ve sonuçların bir dizisini döndürür. Alıcı dizideki öğeler NSDictionaries ise, -valueForKey:, -objectForKey: ile neredeyse aynıdır. Anahtar bir @ ile başlamadığı sürece çalışacaktır.

74
JeremyP

Diğer tüm cevapları özetlemek için:

Yakut (sorudaki gibi):

array.map{|o| o.name}

Obj-C ( valueForKey ile):

[array valueForKey:@"name"];

Obj-C (valueForKeyPath ile, bkz. KVC Toplama Operatörleri ):

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

Obj-C ( enumerateObjectsUsingBlock ile):

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

Swift ( harita ile , kapaklara bakınız )

array.map { $0.name }

Ayrıca, dizileri daha işlevsel bir şekilde kullanmanıza izin veren birkaç kitaplık vardır. Diğer kütüphaneleri kurmak için Cocoa Pod önerilir.

31
tothemario

Bence valueForKeyPath iyi bir seçimdir.

Aşağıda oturup çok güzel örnekler var. Umarım yardımcı olur.

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

Bazı örnek:

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

Ruby uzmanı değilim, bu yüzden% 100 emin değilim, doğru cevap veriyorum, ancak 'harita'nın dizideki her şeye bir şey yaptığı ve sonuçlarla yeni bir dizi ürettiği yorumuna dayanarak, muhtemelen ne olduğunu düşünüyorum istiyorum gibi bir şey:

NSMutableArray *replacementArray = [NSMutableArray array];

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

Dolayısıyla, bloktaki şeyleri dizideki her şeyde gerçekleştirmek için OS X 10.6/iOS 4.0'daki 'bloklar' için yeni desteği kullanıyorsunuz (bunlar daha genel bakışta kapanıyor). Bazı işlemler yapmayı seçip sonucu ayrı bir diziye ekleyin.

10.5 veya iOS 3.x'i desteklemeyi düşünüyorsanız, muhtemelen ilgili kodu nesnenin içine koymak ve makeObjectsPerformSelector: kullanarak kullanmak isteyebilirsiniz: veya en kötüsü, for(NSDictionary *dictionary in existingArray) işlevini kullanarak dizinin manuel bir yinelemesini yapmak.

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


Yayınlanan birkaç cevapta hafif bir gelişme kaydedildi.

  1. Nil için denetler — eşleme yaparken nesneleri kaldırmak için nil kullanabilirsiniz.
  2. Yöntem adı, yöntemin çağrıldığı diziyi değiştirmediğini daha iyi gösterir.
  3. Bu daha çok stil bir şey ama ben IMO bloğun argüman isimlerini geliştirdim
  4. Count için nokta sözdizimi
2
james_womack

Objective-C için bu cevap listesine ObjectiveSugar kütüphanesini eklerdim: https://github.com/supermarin/ObjectiveSugar

Ayrıca, etiket satırı "İnsanlar için ObjectiveC ilaveleri. Yakut tarzı" dır. hangi OP iyi uyması gerekir ;-)

En yaygın kullanım durumum, bir sunucu çağrısı tarafından döndürülen bir sözlüğü basit nesneler dizisine; NSDictionary mesajlarınızdan NSArray of NSString ID numarası almanız:

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

Amaç-C için, bu cevaplar listesine Yüksek Mertebeden Fonksiyonları ekleyeceğim: https://github.com/fanpyi/Higher-Order-Functions ;

Böyle bir JSON dizi studentJSONList var:

[
    {"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
范陆离