Фильтрация NSArray в новый NSArray в Objective-C

У меня есть NSArray, и я хотел бы создать новый NSArray с объектами из исходного массива, которые соответствуют определенным критериям. Критерии определяются функцией, которая возвращает BOOL.

Я могу создать NSMutableArray, выполнить итерацию по исходному массиву и скопировать объекты, которые принимает функция фильтра, а затем создать его неизменяемую версию.

Есть ли способ лучше?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
123
0
114 731
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Ответ принят как подходящий

NSArray и NSMutableArray предоставляют методы для фильтрации содержимого массива. NSArray предоставляет filterArrayUsingPredicate:, который возвращает новый массив, содержащий объекты в приемнике, которые соответствуют указанному предикату. NSMutableArray добавляет filterUsingPredicate:, который оценивает содержимое получателя по указанному предикату и оставляет только совпадающие объекты. Эти методы проиллюстрированы в следующем примере.

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

Я слушал подкаст Папы Смурфа, и Папа Смурф сказал, что ответы должны находиться в StackOverflow, чтобы сообщество могло их оценивать и улучшать.

willc2 17.09.2009 10:32

@mmalc - Может быть, более подходящим, но определенно более удобным для просмотра прямо здесь.

Bryan 04.08.2010 22:37

NSPredicate мертв, да здравствуют блоки! ср. мой ответ ниже.

Clay Bridges 20.10.2011 03:09

Что означает «содержит [c]»? Я всегда вижу [c], но не понимаю, что он делает?

user1007522 20.06.2014 18:41

@ user1007522, [c] делает совпадение нечувствительным к регистру.

jonbauer 22.10.2014 23:03

Предполагая, что все ваши объекты относятся к одному и тому же типу, вы можете добавить метод в качестве категории их базового класса, который вызывает функцию, которую вы используете для своих критериев. Затем создайте объект 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 filterArrayUsingPredicate: [NSPredicate predicateWithFormat: @ "myMethod = TRUE"]]; хватит. Спасибо! (Мне тоже нравятся альтернативы, но этот хорош).

Motti Shneor 18.02.2019 17:01

Если у вас OS X 10.6 / iOS 4.0 или новее, вам, вероятно, лучше использовать блоки, чем NSPredicate. См. -[NSArray indexesOfObjectsPassingTest:] или напишите свою собственную категорию, чтобы добавить удобный метод -select: или -filter: (пример).

Хотите, чтобы кто-то еще написал эту категорию, протестировал ее и т. д.? Проверьте BlocksKit (массив документов). И есть еще примеры много, которые можно найти, скажем, путем поиска, например, "Выбор категории блока nsarray".

Не могли бы вы дополнить свой ответ примером? Веб-сайты блогов имеют тенденцию умирать, когда они вам нужны больше всего.

Dan Abramov 21.03.2012 15:22

Добавлено больше предложений в случае линкрота.

Clay Bridges 21.03.2012 20:12

Защита от гниения ссылок заключается в извлечении релевантного кода и прочего из статей, на которые есть ссылки, а не в добавлении дополнительных ссылок. Сохраните ссылки, но добавьте пример кода.

toolbear 07.06.2012 06:35

@mydogisbox: это демонстрация.

Clay Bridges 19.06.2012 21:25

@ClayBridges Я нашел этот вопрос в поисках способов фильтрации какао. Поскольку в вашем ответе нет примеров кода, мне потребовалось около 5 минут, чтобы просмотреть ваши ссылки, чтобы понять, что блоки не достигнут того, что мне нужно. Если бы код был в ответе, это заняло бы, может быть, 30 секунд. Этот ответ НАМНОГО менее полезен без фактического кода.

N_A 19.06.2012 21:30

@mydogisbox et alia: здесь всего 4 ответа, и, таким образом, достаточно места для блестящего и превосходного собственного ответа на основе кода. Я доволен своим, поэтому, пожалуйста, оставьте его в покое.

Clay Bridges 19.06.2012 23:51

@ClayBridges SO - это пространство для совместной работы, где приветствуется редактирование ответов. Согласно часто задаваемым вопросам, ваше сообщение имеет лицензию creativecommons.org/licenses/by-sa/3.0. stackoverflow.com/faq#editing

N_A 20.06.2012 00:31

@mydogisbox: Извините, но я склонен согласиться с Клэем в этом вопросе. Его ответ получил 12 голосов, так что сообществу он уже нравится, а ваши правки слишком сильно меняются. Опубликуйте свой собственный ответ, если вы считаете, что существующие неадекватны.

Robert Harvey 20.06.2012 01:08

В защиту mydogisbox ваш ответ станет бесполезным, если какая-либо из ссылок сломается, и призыв людей искать что-то на самом деле не отвечает на вопрос.

Robert Harvey 20.06.2012 01:09

@RobertHarvey Я не понимаю, как я слишком сильно изменился. Согласно часто задаваемым вопросам: «этот сайт редактируется совместно, как и Википедия. Если вы видите что-то, что требует улучшения, нажмите« Изменить »и помогите нам сделать это!» Включение связанного кода в ответ является стандартной процедурой для борьбы с гниением ссылок и сокращает время, необходимое для понимания ответа. Единственная часть, которую я удалил или изменил, кроме добавления связанного кода, была плохой ссылкой.

N_A 20.06.2012 01:16

mydogisbox прав в этом вопросе; если все, что вы делаете, - это ссылка, на самом деле это не ответ, даже если вы добавляете больше ссылок. См. meta.stackexchange.com/questions/8231. Лакмусовая бумажка: Может ли ваш ответ стоять сам по себе или для этого нужно щелкать ссылки, чтобы иметь какую-либо ценность? В частности, вы утверждаете, что «вам, вероятно, лучше с блоками, чем с NSPredicate», но вы не объясняете, почему.

Robert Harvey 20.06.2012 01:55

@mydogisbox: Уже есть принятый ответ, который, вероятно, удовлетворительный.

Robert Harvey 20.06.2012 01:58

@RobertHarvey Хммм, ладно. Я думал, что вторичные ответы также нуждаются в улучшении, поскольку они также имеют ценность (как в этом случае). Я исправился.

N_A 20.06.2012 02:05

@RobertHarvey Поскольку вы обычно меня защищаете: спасибо, правда, большое. Я действительно думаю, что ответ сам по себе без ссылок (как и «Используйте вместо этого блоки»), поэтому я думаю, что «станет бесполезным» - это преувеличение. Что касается того, почему и когда блоки лучше, чем NSPredicate, все сводится к сути, ремеслу и мнению, и эти вещи трудно объяснить в то время, когда я планировал бюджет. Таким образом, «вам, вероятно, лучше».

Clay Bridges 20.06.2012 12:35

Для заинтересованных читателей (всех троих) я настоятельно рекомендую нажать Ссылка @ RobertHarvey. Там разгораются споры, и этот вопрос вряд ли решится.

Clay Bridges 20.06.2012 12:37

Принятый ответ по этой ссылке является общепринятым в настоящее время консенсусом. Ваш ответ прошел успешно, потому что за него так много голосов, но мы регулярно удаляем ответы, содержащие всего лишь одну ссылку.

Robert Harvey 20.06.2012 18:46

В чем разница в производительности между подходами на основе предикатов и блоков?

AlexR 19.10.2012 12:37

@AlexR Нет, не знаю. Думаю, лучше задать отдельный вопрос.

Clay Bridges 03.04.2013 01:12

Основываясь на ответе Клея Бриджеса, вот пример фильтрации с использованием блоков (замените yourArray на имя переменной вашего массива, а testFunc на имя вашей функции тестирования):

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

Наконец, ответ не только упоминает фильтрацию с помощью блоков, но также дает хороший пример того, как это сделать. Спасибо.

Krystian 30.01.2016 14:27

Мне нравится этот ответ; хотя filteredArrayUsingPredicate более компактный, тот факт, что вы не используете никаких предикатов, как бы скрывает цель.

James Perih 14.04.2016 00:41

Это самый современный способ сделать это. Лично я скучаю по старому сокращению «objectsPassingTest», которое в какой-то момент исчезло из API. Тем не менее, это работает быстро и хорошо. Мне нравится NSPredicate - но для других вещей, где нужен более тяжелый молоток

Motti Shneor 18.02.2019 16:50

Теперь вы получите ошибку Implicit conversion of 'NSUInteger' (aka 'unsigned long') to 'NSIndexSet * _Nonnull' is disallowed with ARC ... он ожидает NSIndexSets

anoop4real 28.04.2020 11:01

@ anoop4real, я думаю, что причиной предупреждения, о котором вы упомянули, является то, что вы по ошибке использовали indexOfObjectPassingTest вместо indexesOfObjectsPassingTest. Легко пропустить, но большая разница :)

pckill 05.05.2020 13:40

NSPredicate - это новый способ создания условия для фильтрации коллекции (NSArray, NSSet, NSDictionary).

Например, рассмотрим два массива arr и filteredarr:

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) выберите * из таблицы -> сборник

2) column1 как "% a%" -> NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

3) выберите * из таблицы, где столбец1, например '% a%' ->

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

надеюсь, это поможет

Есть множество способов сделать это, но, безусловно, самый изящный - использовать [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.
}]];

Я думаю, что это настолько лаконично, насколько это возможно.


Быстрый:

Для тех, кто работает с NSArray в Swift, вы можете предпочесть эту краткую версию даже больше:

let filteredArray = array.filter { $0.shouldIKeepYou() }

filter - это просто метод на Array (NSArray неявно связан с Array Swift). Он принимает один аргумент: замыкание, которое принимает один объект в массиве и возвращает Bool. В вашем закрытии просто верните true для любых объектов, которые вы хотите в отфильтрованном массиве.

Какую роль здесь играют привязки NSDictionary *?

Kaitain 15.08.2015 07:52

Привязки @Kaitain требуются API NSPredicate predicateWithBlock:.

ThomasW 11.09.2015 05:59

@Kaitain Словарь bindings может содержать привязки переменных для шаблонов.

Amin Negm-Awad 08.12.2015 11:36

Намного полезнее, чем принятый ответ при работе со сложными объектами :)

Ben Leggiero 02.03.2016 01:20

Оформить заказ в этой библиотеке

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;
}];

Лучший и простой способ - создать этот метод и передать массив и значение:

- (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;
}

Другой метод категорий, который вы могли бы использовать:

- (NSArray *) filteredArrayUsingBlock:(BOOL (^)(id obj))block {
    NSIndexSet *const filteredIndexes = [self indexesOfObjectsPassingTest:^BOOL (id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
                                       return block(obj);
                                   }];

    return [self objectsAtIndexes:filteredIndexes];
}

Другие вопросы по теме