IT TIP

지도와 동등한 NSArray

itqueen 2020. 10. 27. 23:56
반응형

지도와 동등한 NSArray


감안할 때 NSArrayNSDictionary그 지정된 키의 배열에 대한지도 작성을 수행 할 수있다 (유사한 목적 및 키를 포함하는) 객체는? 예를 들어 Ruby에서는 다음과 같이 할 수 있습니다.

array.map(&:name)

업데이트 : Swift를 사용하는 경우 map을 참조하십시오 .


BlocksKit 은 옵션입니다.

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

밑줄 은 또 다른 옵션입니다. map기능 이 있습니다. 다음은 웹 사이트의 예입니다.

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;

몇 줄만 저장하지만 NSArray에서 범주를 사용합니다. 블록이 nil을 반환하지 않도록해야하지만 그 외에는 -[NSArray valueForKey:]작동하지 않는 경우 시간을 절약 할 수 있습니다.

@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

사용법은 다음과 같습니다 -[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)

Ruby가 무엇을하는지 모르겠지만 NSArray의 -valueForKey : 구현을 찾고 있다고 생각 합니다 . 이것은 배열의 모든 요소로 보내고 결과의 배열을 반환합니다. 수신 배열의 요소가 NSDictionaries 인 경우 . 키가로 시작하지 않는 한 작동합니다.-valueForKey:-valueForKey:-objectForKey:@


다른 모든 답변을 요약하려면 :

루비 (질문에서와 같이) :

array.map{|o| o.name}

Obj-C ( valueForKey 사용 ) :

[array valueForKey:@"name"];

Obj-C (valueForKeyPath 포함, KVC 컬렉션 연산자 참조 ) :

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

Obj-C ( enumerateObjectsUsingBlock 사용 ) :

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

Swift ( 지도 포함 , 폐쇄 참조 )

array.map { $0.name }

그리고보다 기능적인 방식으로 배열을 처리 할 수있는 몇 가지 라이브러리가 있습니다. Cocoa Pods 는 다른 라이브러리를 설치하는 데 권장됩니다.


valueForKeyPath가 좋은 선택이라고 생각합니다.

아래에 앉아 매우 멋진 예가 있습니다. 도움이 되길 바랍니다.

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

몇 가지 예 :

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

저는 루비 전문가가 아니기 때문에 정확히 대답하고 있다고 100 % 확신하지는 못합니다.하지만 '맵'이 배열의 모든 것에 무언가를하고 그 결과로 새로운 배열을 생성한다는 해석을 바탕으로 여러분이 아마도 원하는 것은 다음과 같습니다.

NSMutableArray *replacementArray = [NSMutableArray array];

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

따라서 OS X 10.6 / iOS 4.0에서 '블록'(보다 일반적인 용어로 클로저)에 대한 새로운 지원을 사용하여 어레이의 모든 항목에 대해 블록의 작업을 수행하고 있습니다. 몇 가지 작업을 수행 한 다음 결과를 별도의 배열에 추가하도록 선택합니다.

10.5 또는 iOS 3.x를 지원하려는 경우 관련 코드를 객체에 넣고 makeObjectsPerformSelector를 사용하는 방법을 살펴 보거나 최악의 경우 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


게시 된 몇 가지 답변에 약간의 개선이 있습니다.

  1. nil을 확인합니다. 매핑 할 때 nil을 사용하여 개체를 제거 할 수 있습니다.
  2. 메서드 이름은 메서드가 호출 된 배열을 수정하지 않는다는 것을 더 잘 반영합니다.
  3. 이것은 스타일에 가깝지만 IMO가 블록의 인수 이름을 개선했습니다.
  4. 카운트에 대한 점 구문

For Objective-C, I would add the ObjectiveSugar library to this list of answers: https://github.com/supermarin/ObjectiveSugar

Plus, its tagline is "ObjectiveC additions for humans. Ruby style." which should suit OP well ;-)

My most common use-case is mapping an dictionary returned by a server call to an array of simpler objects e.g. getting an NSArray of NSString IDs from your NSDictionary posts:

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

For Objective-C, I would add the Higher-Order-Functions to this list of answers: https://github.com/fanpyi/Higher-Order-Functions;

There is a JSON array studentJSONList like this:

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

Swift introduces a new map function.

Here is an example from the documentation:

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

The map function takes a closure which returns a value of any type and maps the existing values in the array to instances of this new type.

참고URL : https://stackoverflow.com/questions/6127638/nsarray-equivalent-of-map

반응형