Filtern von NSArray in ein neues NSArray in Objective-C


121

Ich habe eine NSArrayund möchte eine neue NSArraymit Objekten aus dem ursprünglichen Array erstellen , die bestimmte Kriterien erfüllen. Das Kriterium wird von einer Funktion festgelegt, die a zurückgibt BOOL.

Ich kann ein erstellen NSMutableArray, durch das Quellarray iterieren und über die Objekte kopieren, die die Filterfunktion akzeptiert, und dann eine unveränderliche Version davon erstellen.

Gibt es einen besseren Weg?

Antworten:


140

NSArrayund NSMutableArrayMethoden zum Filtern von Array-Inhalten bereitstellen. NSArraybietet gefiltertesArrayUsingPredicate: Gibt ein neues Array zurück, das Objekte im Empfänger enthält, die mit dem angegebenen Prädikat übereinstimmen. NSMutableArrayfügt filterUsingPredicate hinzu : Hiermit wird der Inhalt des Empfängers anhand des angegebenen Prädikats bewertet und nur übereinstimmende Objekte belassen . Diese Methoden werden im folgenden Beispiel veranschaulicht.

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
Ich habe mir den Podcast von Papa Smurf angehört und Papa Smurf sagte, die Antworten sollten in StackOverflow gespeichert sein, damit die Community sie bewerten und verbessern kann.
Willc2

10
@mmalc - Vielleicht besser geeignet, aber sicherlich bequemer, es hier anzuzeigen.
Bryan

5
NSPredicate ist tot, es lebe die Blöcke! vgl. meine Antwort unten.
Clay Bridges

Was bedeutet enthält [c]? Ich sehe immer das [c], aber ich verstehe nicht, was es tut?
user1007522

3
@ user1007522, das [c] macht das Match
unabhängig von

104

Es gibt viele Möglichkeiten, dies zu tun, aber bei weitem die sauberste verwendet sicherlich [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.
}]];

Ich denke, das ist so kurz wie es nur geht.


Schnell:

Für diejenigen, die mit NSArrays in Swift arbeiten, bevorzugen Sie möglicherweise diese noch präzisere Version:

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

filterist nur eine Methode auf Array( NSArrayist implizit mit Swifts verbunden Array). Es braucht ein Argument: einen Abschluss, der ein Objekt im Array nimmt und a zurückgibt Bool. trueKehren Sie in Ihrem Abschluss einfach für alle gewünschten Objekte im gefilterten Array zurück.


1
Welche Rolle spielen NSDictionary * -Bindungen hier?
Kaitain

@ Kaitain-Bindungen werden von der NSPredicate predicateWithBlock:API benötigt.
ThomasW

@Kaitain Das bindingsWörterbuch kann variable Bindungen für Vorlagen enthalten.
Amin Negm-Awad

Viel nützlicher als akzeptierte Antwort beim Umgang mit komplexen Objekten :)
Ben Leggiero

47

Basierend auf einer Antwort von Clay Bridges finden Sie hier ein Beispiel für das Filtern mithilfe von Blöcken (ändern yourArraySie testFuncden Namen Ihrer Array-Variablen und den Namen Ihrer Testfunktion):

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

Zum Schluss eine Antwort, in der nicht nur das Filtern mit Blöcken erwähnt wird, sondern auch ein gutes Beispiel dafür gegeben wird. Vielen Dank.
Krystian

Ich mag diese Antwort; Auch wenn filteredArrayUsingPredicatees schlanker ist, verdunkelt die Tatsache, dass Sie keine Prädikate verwenden, den Zweck.
James Perih

Dies ist der modernste Weg, dies zu tun. Persönlich sehne ich mich nach der alten Abkürzung "objectsPassingTest", die zu einem bestimmten Zeitpunkt aus der API verschwunden ist. Trotzdem läuft das schnell und gut. Ich mag NSPredicate - aber für andere Dinge, bei denen der schwerere Hammer benötigt wird
Motti Shneor

Sie werden jetzt eine Fehlermeldung erhalten Implicit conversion of 'NSUInteger' (aka 'unsigned long') to 'NSIndexSet * _Nonnull' is disallowed with ARC... es werden NSIndexSets erwartet
anoop4real

@ anoop4real, ich denke, die Ursache für die Warnung, die Sie erwähnt haben, ist, dass Sie fälschlicherweise indexOfObjectPassingTestanstelle von verwendet haben indexesOfObjectsPassingTest. Leicht zu übersehen, aber großer Unterschied :)
pckill

46

Wenn Sie OS X 10.6 / iOS 4.0 oder höher sind, sind Sie mit Blöcken wahrscheinlich besser dran als mit NSPredicate. Sehen -[NSArray indexesOfObjectsPassingTest:]oder schreiben Sie Ihre eigene Kategorie, um ein Handy -select:oder eine -filter:Methode hinzuzufügen ( Beispiel ).

Möchten Sie, dass jemand anderes diese Kategorie schreibt, testet usw.? Schauen Sie sich BlocksKit ( Array-Dokumente ) an. Und es gibt noch viele weitere Beispiele, die beispielsweise durch die Suche nach "nsarray block category select" gefunden werden können .


5
Würde es Ihnen etwas ausmachen, Ihre Antwort mit einem Beispiel zu erweitern? Blog-Websites neigen dazu zu sterben, wenn Sie sie am dringendsten benötigen.
Dan Abramov

8
Der Schutz gegen Link Rot besteht darin, relevanten Code und so weiter aus den verlinkten Artikeln zu extrahieren und keine weiteren Links hinzuzufügen. Behalten Sie die Links bei, aber fügen Sie einen Beispielcode hinzu.
Toolbear

3
@ClayBridges Ich habe diese Frage gefunden und nach Möglichkeiten gesucht, in Kakao zu filtern. Da Ihre Antwort keine Codebeispiele enthält, habe ich ungefähr 5 Minuten gebraucht, um Ihre Links zu durchsuchen und herauszufinden, dass Blöcke nicht das erreichen, was ich brauche. Wenn der Code in der Antwort gewesen wäre, hätte es vielleicht 30 Sekunden gedauert. Diese Antwort ist ohne den eigentlichen Code weitaus weniger nützlich.
N_A

1
@mydogisbox et alia: Hier gibt es nur 4 Antworten und somit viel Platz für eine glänzende und überlegene Antwort mit Code-Sampling. Ich bin zufrieden mit meinem, also lass es bitte in Ruhe.
Clay Bridges

2
mydogisbox ist in diesem Punkt richtig; Wenn Sie lediglich einen Link bereitstellen, ist dies keine wirkliche Antwort, selbst wenn Sie weitere Links hinzufügen. Siehe meta.stackexchange.com/questions/8231 . Der Lackmustest: Kann Ihre Antwort für sich allein stehen oder muss auf die Links geklickt werden, um einen Wert zu haben? Insbesondere geben Sie an, dass Sie mit Blöcken wahrscheinlich besser dran sind als mit NSPredicate, erklären aber nicht wirklich, warum.
Robert Harvey

16

Angenommen, Ihre Objekte haben alle einen ähnlichen Typ, können Sie eine Methode als Kategorie ihrer Basisklasse hinzufügen, die die Funktion aufruft, die Sie für Ihre Kriterien verwenden. Erstellen Sie dann ein NSPredicate-Objekt, das auf diese Methode verweist.

Definieren Sie in einigen Kategorien Ihre Methode, die Ihre Funktion verwendet

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

Dann, wo immer Sie filtern:

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

Wenn Ihre Funktion nur mit Eigenschaften verglichen wird, die innerhalb Ihrer Klasse erreichbar sind, ist es natürlich möglicherweise einfacher, die Bedingungen der Funktion in eine Prädikatzeichenfolge zu konvertieren.


Ich mochte diese Antwort, weil sie anmutig und kurz ist und die Notwendigkeit verkürzt, erneut zu lernen, wie man ein NSPredikat für das Grundlegendste formalisiert - den Zugriff auf Eigenschaften und die Boolesche Methode. Ich denke sogar, dass der Wrapper nicht benötigt wurde. Ein einfaches [myArray filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @ "myMethod = TRUE"]]; würde genügen. Vielen Dank! (Ich liebe auch die Alternativen, aber diese ist schön).
Motti Shneor

3

NSPredicateist der Weg der Nextstep Bedingung Filter eine Sammlung von Konstruieren ( NSArray, NSSet, NSDictionary).

Betrachten Sie zum Beispiel zwei Arrays arrund filteredarr:

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

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

Der Filteredarr wird sicherlich nur die Elemente haben, die das Zeichen c enthalten.

um es einfach zu machen, sich an diejenigen zu erinnern, die wenig SQL-Hintergrund haben

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

1) Wählen Sie * aus tbl -> Sammlung

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

3) Wählen Sie * aus tbl, wobei Spalte1 '% a%' -> ist

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

ich hoffe das hilft


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

Sie können diese Kategorie hier herunterladen


0

Kasse dieser Bibliothek

https://github.com/BadChoice/Collection

Es kommt mit vielen einfachen Array-Funktionen, um nie wieder eine Schleife zu schreiben

Sie können also einfach Folgendes tun:

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

oder

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

0

Der beste und einfachste Weg ist, diese Methode zu erstellen und Array und Wert zu übergeben:

- (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;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.