NSArray'i Objective-C'de yeni bir NSArray olarak filtreleme


121

Bir var NSArrayve NSArrayorijinal diziden belirli kriterleri karşılayan nesnelerle yeni bir tane oluşturmak istiyorum . Ölçüt, a döndüren bir işlev tarafından belirlenir BOOL.

NSMutableArrayKaynak dizide bir yineleme oluşturabilir , filtre işlevinin kabul ettiği nesnelerin üzerine kopyalayabilir ve ardından onun değişmez bir sürümünü oluşturabilirim.

Daha iyi bir yol var mı?

Yanıtlar:


140

NSArrayve NSMutableArraydizi içeriklerini filtrelemek için yöntemler sağlar. NSArraysağlar filteredArrayUsingPredicate: alıcıda belirtilen yüklemle eşleşen nesneleri içeren yeni bir dizi döndürür. NSMutableArrayadds filterUsingPredicate: alıcının içeriğini belirtilen koşula göre değerlendirir ve yalnızca eşleşen nesneleri bırakır. Bu yöntemler aşağıdaki örnekte gösterilmiştir.

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
Şirin Baba'nın podcast'ini dinledim ve Şirin Baba, cevapların StackOverflow'da yaşaması gerektiğini söyledi, böylece topluluk onları derecelendirebilir ve iyileştirebilir.
willc2

10
@mmalc - Belki daha uygun, ama onu burada görmek kesinlikle daha uygun.
Bryan

5
NSPredicate öldü, uzun ömürlü bloklar! bakınız aşağıdaki cevabım.
Kil Köprüler

İçerir [c] ne anlama geliyor? Her zaman [c] 'yi görüyorum ama ne yaptığını anlamıyorum?
user1007522

3
@ user1007522, [c] maçı büyük / küçük harfe duyarlı hale getiriyor
jonbauer

104

Bunu yapmanın pek çok yolu var, ancak en temiz olanı kesinlikle şunu kullanıyor [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.
}]];

Sanırım bu olabildiğince özlü.


Swift:

NSArraySwift'de s ile çalışanlar için , bu daha kısa versiyonu tercih edebilirsiniz :

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

filtersadece bir yöntemdir Array( NSArrayörtük olarak Swift'inkine bağlanmıştır Array). Bir argüman alır: dizideki bir nesneyi alan ve a döndüren bir kapanış Bool. Kapanışınızda, truefiltrelenmiş dizide istediğiniz nesneler için geri dönmeniz yeterlidir.


1
NSDictionary * bağlamalarının burada rolü nedir?
Kaitain

@Kaitain bağlamaları NSPredicate predicateWithBlock:API tarafından gereklidir .
ThomasW

@Kaitain bindingsSözlük, şablonlar için değişken bağlar içerebilir.
Amin Negm-Awad

Karmaşık nesnelerle uğraşırken kabul edilen cevaptan çok daha yararlı :)
Ben Leggiero

47

Clay Bridges'in bir cevabına göre, blokları kullanarak filtrelemenin bir örneğini burada bulabilirsiniz ( yourArraydizi değişkeninizin adını ve testFunctest işlevinizin adını değiştirin):

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

Son olarak, sadece bloklarla filtrelemeden bahsetmeyen, aynı zamanda nasıl yapılacağına dair iyi bir örnek veren bir cevap. Teşekkürler.
Krystian

Bu cevabı beğendim; daha filteredArrayUsingPredicateyalın olsa bile , herhangi bir yüklem kullanmamanız, amacı bir şekilde belirsizleştirir.
James Perih

Bunu yapmanın en modern yolu budur. Kişisel olarak, belirli bir noktada API'den kaybolan eski "objectsPassingTest" kısaltmasını özlüyorum. Yine de bu hızlı ve iyi çalışıyor.
NSPredicate'i

Şimdi hata alacaksınız Implicit conversion of 'NSUInteger' (aka 'unsigned long') to 'NSIndexSet * _Nonnull' is disallowed with ARC... NSIndexSets'i bekliyor
anoop4real

@ anoop4real, sanırım bahsettiğiniz uyarının nedeni, indexOfObjectPassingTestyerine yanlışlıkla kullanmış olmanızdır indexesOfObjectsPassingTest.
Kaçırması

46

OS X 10.6 / iOS 4.0 veya sonraki bir sürümü iseniz, muhtemelen NSPredicate'den bloklarla daha iyi durumda olursunuz. -[NSArray indexesOfObjectsPassingTest:]Kullanışlı bir yöntem -select:veya -filter:yöntem ( örnek ) eklemek için kendi kategorinizi görün veya yazın .

Bu kategoriyi başka birinin yazmasını, test etmesini vb. Mi istiyorsunuz? BlocksKit'e ( dizi belgeleri ) göz atın . Ve çok var örneğin "nsarray blok kategorisi seçimi" için arama yaparak bulunabilecek daha örnek .


5
Cevabınızı bir örnekle genişletebilir misiniz? Blog web siteleri, onlara en çok ihtiyaç duyduğunuzda ölme eğilimindedir.
Dan Abramov

8
Bağlantı çürümesine karşı koruma, bağlantılı makalelerden ilgili kodu ve ne olmadığını almak, daha fazla bağlantı eklemek değil. Bağlantıları saklayın, ancak bazı örnek kodlar ekleyin.
toolbear

3
@ClayBridges Bu soruyu kakao içinde filtreleme yolları ararken buldum. Cevabınızda herhangi bir kod örneği bulunmadığından, blokların ihtiyacım olanı elde edemeyeceğini bulmak için bağlantılarınızı araştırmam yaklaşık 5 dakika sürdü. Kod cevapta olsaydı belki 30 saniye sürerdi. Bu cevap, gerçek kod olmadan çok daha az faydalıdır.
N_A

1
@mydogisbox ve diğerleri: Burada yalnızca 4 yanıt var ve bu nedenle, kendi başınıza parlak ve üstün kod örneklemeli bir yanıt için bolca yer var. Benimkiyle mutluyum, bu yüzden lütfen rahat bırakın.
Clay Bridges

2
mydogisbox tam bu noktada; Yaptığınız tek şey bir bağlantı sağlamaksa, daha fazla bağlantı ekleseniz bile bu gerçekten bir cevap değildir. Meta.stackexchange.com/questions/8231 adresine bakın . Turnusol testi: Cevabınız tek başına kalabilir mi, yoksa herhangi bir değere sahip olmak için bağlantılara tıklamayı mı gerektiriyor? Özellikle, "muhtemelen bloklarla NSPredicate'ten daha iyisiniz" diyorsunuz, ancak nedenini gerçekten açıklamıyorsunuz.
Robert Harvey

16

Nesnelerinizin hepsinin benzer türde olduğunu varsayarsak, ölçütleriniz için kullandığınız işlevi çağıran temel sınıflarının bir kategorisi olarak bir yöntem ekleyebilirsiniz. Ardından, bu yönteme başvuran bir NSPredicate nesnesi oluşturun.

Bazı kategorilerde, işlevinizi kullanan yönteminizi tanımlayın

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

Sonra nerede filtreleyecekseniz:

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

Elbette, işleviniz yalnızca sınıfınızın içinden erişilebilen özelliklerle karşılaştırırsa, işlevin koşullarını bir yüklem dizesine dönüştürmek daha kolay olabilir.


Bu yanıtı beğendim, çünkü zarif ve kısa ve bir NSPredicate'i en temel şey için - özelliklere ve boolean yöntemine erişme - yeniden nasıl resmileştirileceğini tekrar öğrenme ihtiyacı. Hatta paketleyiciye ihtiyaç olmadığını düşünüyorum. Basit bir [myArray filteredArrayUsingPredicate: [NSPredicate yüklemWithFormat: @ "myMethod = TRUE"]]; yeterli olur. Teşekkürler! (Alternatifleri de seviyorum ama bu güzel).
Motti Shneor

3

NSPredicate(bir koleksiyon filtrelemek için koşul oluşturmanın NeXTSTEP'in yoludur NSArray, NSSet,NSDictionary ).

Örneğin iki diziyi düşünün arrve filteredarr:

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

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

filteredarr kesinlikle sadece c karakterini içeren öğelere sahip olacaktır.

küçük sql arka planı olanların hatırlanmasını kolaylaştırmak için

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

1) tbl'den * seçin -> koleksiyon

2) sütun1 '% a%' gibi ->NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

3) tbl'den * seçin, burada sütun1 '% a%' gibi ->

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

Umarım bu yardımcı olur


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

Bu kategoriyi buradan indirebilirsiniz


0

Bu kitaplığa göz atın

https://github.com/BadChoice/Collection

Bir daha asla döngü yazmamak için birçok kolay dizi işleviyle birlikte gelir

Yani şunları yapabilirsiniz:

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

veya

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

0

En iyi ve kolay yol, bu yöntemi oluşturmak ve Dizi ve Değer iletmektir:

- (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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.