memfilter NSArray menjadi NSArray baru di Objective-C

121

Saya punya NSArraydan saya ingin membuat yang baru NSArraydengan objek dari larik asli yang memenuhi kriteria tertentu. Kriteria ditentukan oleh fungsi yang mengembalikan a BOOL.

Saya dapat membuat NSMutableArray, mengulang melalui larik sumber dan menyalin objek yang diterima fungsi filter dan kemudian membuat versi yang tidak dapat diubah.

Apakah ada cara yang lebih baik?

lajos
sumber

Jawaban:

140

NSArraydan NSMutableArraymenyediakan metode untuk memfilter konten array. NSArraymenyediakan filteredArrayUsingPredicate: yang mengembalikan larik baru yang berisi objek di penerima yang cocok dengan predikat yang ditentukan.NSMutableArrayMenambahkan filterUsingPredicate: yang mengevaluasi isi penerima terhadap predikat tertentu dan daun hanya benda pertandingan itu. Metode ini diilustrasikan dalam contoh berikut.

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" }
lajos
sumber
84
Saya mendengarkan podcast Papa Smurf dan Papa Smurf mengatakan jawaban harus ada di StackOverflow sehingga komunitas dapat menilai dan memperbaikinya.
willc2
10
@mmalc - Mungkin lebih sesuai, tapi tentu lebih nyaman untuk melihatnya di sini.
Bryan
5
NSPredicate sudah mati, blok hidup panjang! cf. jawaban saya di bawah.
Clay Bridges
Apa yang dimaksud dengan [c]? Saya selalu melihat [c] tapi saya tidak mengerti apa fungsinya?
pengguna1007522
3
@ user1007522, [c] membuat pertandingan peka huruf besar / kecil
jonbauer
104

Ada banyak cara untuk melakukan ini, tetapi sejauh ini yang paling rapi pasti menggunakan [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.
}]];

Saya pikir itu sesingkat yang didapat.


Cepat:

Bagi mereka yang bekerja dengan NSArrays di Swift, Anda mungkin lebih suka versi yang lebih ringkas ini:

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

filterhanyalah sebuah metode Array( NSArraysecara implisit dihubungkan dengan Swift Array). Dibutuhkan satu argumen: penutupan yang mengambil satu objek dalam array dan mengembalikan a Bool. Dalam closure Anda, cukup kembalikan trueobjek apa pun yang Anda inginkan dalam array yang difilter.

Stuart
sumber
1
Peran apa yang dimainkan oleh NSDictionary * binding di sini?
Kaitain
Binding @kain diperlukan oleh NSPredicate predicateWithBlock:API.
ThomasW
@Kamus_kamus bindingsdapat berisi pengikatan variabel untuk template.
Amin Negm-Awad
Jauh lebih berguna daripada jawaban yang diterima ketika berhadapan dengan objek yang kompleks :)
Ben Leggiero
47

Berdasarkan jawaban dari Clay Bridges, berikut adalah contoh pemfilteran menggunakan blok (ubah yourArrayke nama variabel array Anda dan testFuncke nama fungsi pengujian Anda):

yourArray = [yourArray objectsAtIndexes:[yourArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return [self testFunc:obj];
}]];
pckill
sumber
Akhirnya jawaban tidak hanya menyebutkan penyaringan dengan blok, tetapi juga memberikan contoh yang baik tentang bagaimana melakukannya. Terima kasih.
Krystian
Saya suka jawaban ini; Meskipun filteredArrayUsingPredicatelebih ramping, fakta bahwa Anda tidak menggunakan predikat apa pun mengaburkan tujuannya.
James Perih
Ini adalah cara paling modern untuk melakukan ini. Secara pribadi saya merindukan singkatan lama "objectPassingTest" yang menghilang dari API pada titik tertentu. Tetap saja ini berjalan cepat dan bagus. Saya suka NSPredicate - tetapi untuk hal-hal lain, di mana palu yang lebih berat dibutuhkan
Motti Shneor
Anda akan mendapatkan kesalahan sekarang Implicit conversion of 'NSUInteger' (aka 'unsigned long') to 'NSIndexSet * _Nonnull' is disallowed with ARC... NSIndexSets mengharapkan
anoop4real
@ anoop4real, saya pikir penyebab peringatan yang Anda sebutkan adalah bahwa Anda keliru digunakan indexOfObjectPassingTestsebagai pengganti indexesOfObjectsPassingTest. Mudah terlewatkan, tetapi perbedaan besar :)
pckill
46

Jika Anda menggunakan OS X 10.6 / iOS 4.0 atau yang lebih baru, Anda mungkin lebih baik menggunakan blok daripada NSPredicate. Lihat -[NSArray indexesOfObjectsPassingTest:]atau tulis kategori Anda sendiri untuk menambahkan cara -select:atau -filter:metode ( contoh ).

Ingin orang lain menulis kategori itu, mengujinya, dll.? Lihat BlocksKit ( dokumen array ). Dan masih banyak lagi contoh yang bisa ditemukan, katakanlah, mencari misalnya "kategori blok nsarray pilih" .

Clay Bridges
sumber
5
Maukah Anda memperluas jawaban Anda dengan sebuah contoh? Situs web blog cenderung mati saat Anda sangat membutuhkannya.
Dan Abramov
8
Perlindungan terhadap pembusukan tautan adalah mengutip kode yang relevan dan yang lainnya dari artikel yang ditautkan, bukan menambahkan lebih banyak tautan. Pertahankan tautan, tetapi tambahkan beberapa kode contoh.
toolbear
3
@ClayBridges Saya menemukan pertanyaan ini mencari cara untuk menyaring kakao. Karena jawaban Anda tidak memiliki sampel kode di dalamnya, saya butuh waktu sekitar 5 menit untuk menggali melalui tautan Anda untuk mengetahui bahwa blok tidak akan mencapai apa yang saya butuhkan. Jika kodenya ada dalam jawaban itu akan memakan waktu mungkin 30 detik. Jawaban ini JAUH kurang bermanfaat tanpa kode sebenarnya.
N_A
1
@mydogisbox et alia: hanya ada 4 jawaban di sini, dan karenanya banyak ruang untuk jawaban sampel-kode yang berkilau & unggul dari Anda sendiri. Saya senang dengan milik saya, jadi tolong biarkan saja.
Clay Bridges
2
mydogisbox benar dalam hal ini; jika yang Anda lakukan hanyalah memberikan tautan, itu bukanlah jawaban, bahkan jika Anda menambahkan lebih banyak tautan. Lihat meta.stackexchange.com/questions/8231 . Tes lakmus: Dapatkah jawaban Anda berdiri sendiri, atau apakah perlu mengklik tautan agar ada nilainya? Secara khusus, Anda menyatakan "Anda mungkin lebih baik dengan blok daripada NSPredicate," tetapi Anda tidak benar-benar menjelaskan mengapa.
Robert Harvey
16

Dengan asumsi bahwa semua objek Anda berjenis serupa, Anda dapat menambahkan metode sebagai kategori kelas dasarnya yang memanggil fungsi yang Anda gunakan untuk kriteria Anda. Kemudian buat objek NSPredicate yang mengacu pada metode itu.

Dalam beberapa kategori, tentukan metode Anda yang menggunakan fungsi Anda

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

Lalu di mana pun Anda akan memfilter:

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

Tentu saja, jika fungsi Anda hanya membandingkan dengan properti yang dapat dijangkau dari dalam kelas Anda, mungkin akan lebih mudah untuk mengubah kondisi fungsi menjadi string predikat.

Ashley Clark
sumber
Saya menyukai jawaban ini, karena ini anggun dan pendek, dan jalan pintas kebutuhan untuk belajar lagi bagaimana memformalkan NSPredicate untuk hal yang paling dasar - mengakses properti dan metode boolean. Saya bahkan berpikir bungkusnya tidak diperlukan. Sebuah [myArray filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @ "myMethod = TRUE"]] sederhana; sudah cukup. Terima kasih! (Saya suka alternatifnya juga, tapi yang ini bagus).
Motti Shneor
3

NSPredicatecara NeXTSTEP ini membangun kondisi untuk menyaring koleksi ( NSArray, NSSet,NSDictionary ).

Misalnya pertimbangkan dua larik arrdan filteredarr:

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

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

filteredarr pasti akan memiliki item yang berisi karakter c saja.

agar mudah diingat orang-orang yang memiliki latar belakang sql kecil

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

1) pilih * dari tbl -> koleksi

2) kolom1 seperti '% a%' ->NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

3) pilih * dari tbl dimana kolom1 seperti '% a%' ->

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

saya harap ini membantu

Durai Amuthan.H
sumber
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

Anda dapat mendownload kategori ini di sini

skywinder
sumber
0

Lihat perpustakaan ini

https://github.com/BadChoice/Collection

Muncul dengan banyak fungsi array yang mudah untuk tidak pernah menulis loop lagi

Jadi Anda bisa melakukan:

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

atau

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

Cara terbaik dan mudah adalah dengan membuat metode ini Dan Lewati Array Dan Nilai:

- (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;
}
jalmatari.dll
sumber