Objective-C에서 NSArray를 새 NSArray로 필터링


121

나는이 NSArray나는 새를 만들 싶습니다 NSArray특정 기준을 충족 원래 배열에서 객체. 기준은를 반환하는 함수에 의해 결정됩니다 BOOL.

을 만들고 NSMutableArray소스 배열을 반복하고 필터 함수가 허용하는 객체를 복사 한 다음 변경 불가능한 버전을 만들 수 있습니다.

더 좋은 방법이 있습니까?

답변:


140

NSArrayNSMutableArray필터 어레이 목차 방법을 제공한다. 지정된 술어와 일치하는 수신자의 오브젝트를 포함하는 새 배열을 리턴하는 filtersArrayUsingPredicate :NSArray제공합니다 . filterUsingPredicate : 추가 : 지정된 조건 자에 대해 수신자의 콘텐츠를 평가하고 일치하는 개체 만 남겨 둡니다. 이러한 방법은 다음 예제에 설명되어 있습니다.NSMutableArray

NSMutableArray *array =
    [NSMutableArray arrayWithObjects:@"Bill", @"Ben", @"Chris", @"Melissa", nil];

NSPredicate *bPredicate =
    [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
NSArray *beginWithB =
    [array filteredArrayUsingPredicate:bPredicate];
// beginWithB contains { @"Bill", @"Ben" }.

NSPredicate *sPredicate =
    [NSPredicate predicateWithFormat:@"SELF contains[c] 's'"];
[array filteredArrayUsingPredicate:sPredicate];
// array now contains { @"Chris", @"Melissa" }

84
저는 Papa Smurf의 팟 캐스트를 들었고 Papa Smurf는 답변이 StackOverflow에 있어야 커뮤니티가 평가하고 개선 할 수 있다고 말했습니다.
willc2

10
@mmalc-아마도 더 적절할 수도 있지만 바로 여기에서 보는 것이 더 편리합니다.
Bryan

5
NSPredicate는 죽었습니다. cf. 아래 내 대답.
Clay Bridges

contains [c]는 무슨 뜻인가요? 나는 항상 [c]를 보지만 그것이 무엇을하는지 이해하지 못합니까?
user1007522

3
@ user1007522, [c]는 대소 문자를 구분하지 않습니다
jonbauer 2014-10-22

104

이 작업을 수행하는 방법에는 여러 가지가 있지만 가장 가까운 방법은 확실히 다음을 사용하고 있습니다 [NSPredicate predicateWithBlock:].

NSArray *filteredArray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
    return [object shouldIKeepYou];  // Return YES for each object you want in filteredArray.
}]];

나는 그것이 얻는 것처럼 간결하다고 생각합니다.


빠른:

NSArraySwift에서 s로 작업하는 사람들 은 이보다 간결한 버전을 선호 할 수 있습니다 .

nsArray = nsArray.filter { $0.shouldIKeepYou() }

filterArray( NSArray은 암시 적으로 Swift에 연결 되는 방법입니다 Array). 하나의 인수를 취합니다 : 배열에서 하나의 객체를 취하고 Bool. 클로저 true에서 필터링 된 배열에서 원하는 객체를 반환하십시오 .


1
여기서 NSDictionary * bindings의 역할은 무엇입니까?
Kaitain

@Kaitain 바인딩은 NSPredicate predicateWithBlock:API에 필요 합니다.
ThomasW 2015 년

@Kaitain bindings사전에는 템플릿에 대한 변수 바인딩이 포함될 수 있습니다.
아민 Negm - 아 와드

허용 대답보다 훨씬 더 유용합니다 :) 복잡한 객체를 처리 할 때
벤 Leggiero

47

Clay Bridges의 답변에 따라 다음은 블록을 사용한 필터링의 예입니다 ( yourArray배열 변수 이름과 testFunc테스트 함수 이름으로 변경 ).

yourArray = [yourArray objectsAtIndexes:[yourArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return [self testFunc:obj];
}]];

마지막으로 블록 필터링을 언급 할뿐만 아니라이를 수행하는 방법에 대한 좋은 예도 제공합니다. 감사.
Krystian

나는이 대답을 좋아한다. filteredArrayUsingPredicate더 간결 하지만 어떤 술어도 사용하지 않는다는 사실은 목적을 모호하게합니다.
James Perih

이것이 가장 현대적인 방법입니다. 개인적으로 나는 특정 시점에서 API에서 사라진 오래된 속기 "objectsPassingTest"를 그리워합니다. 여전히 이것은 빠르고 좋습니다. 나는 NSPredicate를 좋아합니다. 그러나 더 무거운 망치가 필요한 다른 것들에 대해서는
Motti Shneor

이제 오류가 발생합니다 Implicit conversion of 'NSUInteger' (aka 'unsigned long') to 'NSIndexSet * _Nonnull' is disallowed with ARC... NSIndexSets 예상
anoop4real

@ anoop4real, 당신이 언급 한 경고의 원인 indexOfObjectPassingTestindexesOfObjectsPassingTest. 놓치기 쉽지만 큰 차이점 :)
pckill

46

OS X 10.6 / iOS 4.0 이상인 경우 NSPredicate보다 블록을 사용하는 것이 좋습니다. 보다-[NSArray indexesOfObjectsPassingTest:] 또는 편리한 추가 자신의 범주를 쓰기 -select:또는 -filter:방법 ( ).

다른 사람이 해당 카테고리를 작성하고 테스트하는 등의 작업을 원하십니까? BlocksKit ( array docs )을 확인하십시오 . 그리고 거기에 많은 예를 들어 검색 말하자면 찾을 수 더 많은 예제, "NSArray를 블록 카테고리 선택이" .


5
예를 들어 답변을 확장 해 주시겠습니까? 블로그 웹 사이트는 가장 필요할 때 죽는 경향이 있습니다.
Dan Abramov

8
링크 부패에 대한 보호는 링크를 더 추가하는 것이 아니라 링크 된 기사에서 관련 코드를 발췌하는 것입니다. 링크를 유지하되 예제 코드를 추가하십시오.
toolbear

3
@ClayBridges 코코아에서 필터링하는 방법을 찾는이 질문을 찾았습니다. 귀하의 답변에는 코드 샘플이 없기 때문에 블록이 필요한 것을 달성하지 못함을 확인하기 위해 링크를 파헤치는 데 약 5 분이 걸렸습니다. 코드가 답에 있었다면 아마 30 초가 걸렸을 것입니다. 이 답변은 실제 코드 없이는 덜 유용합니다.
N_A

1
@mydogisbox 외 : 여기에는 답이 4 개뿐이므로 자신 만의 반짝이고 우수한 코드 샘플링 답변을위한 충분한 공간이 있습니다. 나는 내 것이 만족 스럽기 때문에 내버려 두십시오.
Clay Bridges

2
mydogisbox가 바로 여기에 있습니다. 링크를 제공하는 것뿐이라면 링크를 더 추가하더라도 실제로는 답이 아닙니다. meta.stackexchange.com/questions/8231을 참조하세요 . 리트머스 테스트 : 당신의 대답은 그 자체로 설 수 있습니까, 아니면 어떤 가치가있는 링크를 클릭해야합니까? 특히, "NSPredicate보다 블록을 사용하는 것이 더 낫다"고 말하지만 실제로 그 이유를 설명하지는 않습니다.
Robert Harvey

16

객체가 모두 유사한 유형이라고 가정하면 기준에 사용중인 함수를 호출하는 기본 클래스의 범주로 메서드를 추가 할 수 있습니다. 그런 다음 해당 메서드를 참조하는 NSPredicate 개체를 만듭니다.

일부 범주에서 함수를 사용하는 방법을 정의하십시오.

@implementation BaseClass (SomeCategory)
- (BOOL)myMethod {
    return someComparisonFunction(self, whatever);
}
@end

그런 다음 필터링 할 위치 :

- (NSArray *)myFilteredObjects {
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"myMethod = TRUE"];
    return [myArray filteredArrayUsingPredicate:pred];
}

물론 함수가 클래스 내에서 도달 할 수있는 속성과 만 비교하는 경우 함수의 조건을 조건 자 문자열로 변환하는 것이 더 쉬울 수 있습니다.


이 답변은 우아하고 짧기 때문에 가장 기본적인 것에 대해 NSPredicate를 공식화하는 방법, 즉 속성 및 부울 메서드에 액세스하는 방법을 다시 배울 필요가 있기 때문에이 답변을 좋아했습니다. 래퍼도 필요 없다고 생각합니다. 간단한 [myArray filtersArrayUsingPredicate : [NSPredicate predicateWithFormat : @ "myMethod = TRUE"]]; 충분합니다. 감사! (나는 대안도 좋아하지만 이것은 좋은 것입니다).
Motti Shneor

3

NSPredicate(컬렉션을 필터링 조건을 구성하는 넥스트 스텝의 방법입니다 NSArray, NSSet,NSDictionary ).

예를 들어 두 개의 배열을 고려 arrfilteredarr:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

filteredarr = [NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];

filterarr에는 문자 c 만 포함 된 항목이 확실히 있습니다.

SQL 배경이 적은 사람들을 쉽게 기억할 수 있도록

*--select * from tbl where column1 like '%a%'--*

1) tbl- > 컬렉션 에서 * 선택

2) '% a %'와 같은 column1- >NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

3) select * from tbl where column1 like '% a %' ->

[NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];

이게 도움이 되길 바란다


2

NSArray + Xh

@interface NSArray (X)
/**
 *  @return new NSArray with objects, that passing test block
 */
- (NSArray *)filteredArrayPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate;
@end

NSArray + Xm

@implementation NSArray (X)

- (NSArray *)filteredArrayPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
{
    return [self objectsAtIndexes:[self indexesOfObjectsPassingTest:predicate]];
}

@end

여기에서이 카테고리를 다운로드 할 수 있습니다.


0

이 라이브러리 체크 아웃

https://github.com/BadChoice/Collection

루프를 다시 작성하지 않기 위해 많은 쉬운 배열 함수가 제공됩니다.

따라서 다음을 수행 할 수 있습니다.

NSArray* youngHeroes = [self.heroes filter:^BOOL(Hero *object) {
    return object.age.intValue < 20;
}];

또는

NSArray* oldHeroes = [self.heroes reject:^BOOL(Hero *object) {
    return object.age.intValue < 20;
}];

0

가장 좋고 쉬운 방법은이 방법을 만들고 배열 및 값을 전달하는 것입니다.

- (NSArray *) filter:(NSArray *)array where:(NSString *)key is:(id)value{
    NSMutableArray *temArr=[[NSMutableArray alloc] init];
    for(NSDictionary *dic in self)
        if([dic[key] isEqual:value])
            [temArr addObject:dic];
    return temArr;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.