JS. Просто о сложном: filter, map, reduce
Функции высшего порядка — элегантное решение, которое делает код проще, понятнее и эффективнее. Также при первом знакомстве они — верный источник головной боли, от которой не спасают ни гайды в интернете, ни попытки объяснить происходящее словами «свертка» и «отображение».
Этому весьма способствует сложившаяся традиция демонстрировать логику таких функций абстрактными примерами, в которых суммируются какие-то a и b:
Я предлагаю разобрать принцип действия filter, map и reduce на примере, приближенном к жизни и наконец-то разложить все по полочкам.
Жизненный пример
Представим, что, отчаявшись разобраться с функциями высшего порядка самостоятельно, вы создаете топик в блоге с приблизительно таким заголовком:
Видя количество восклицательных знаков в вашем предложении, неравнодушные пользователи бросаются делиться ценными советами. Они пишут комментарии, и, с точки зрения сайта, на котором все происходит (ну ладно, конечно, это Хекслет), комментарии пользователей складываются в один большой массив:
Как видите, массив представлен объектами, каждый из которых описывает один комментарий: кто его написал, какой у этого пользователя рейтинг, что именно написано и так далее (конечно, в реальности все немного сложнее, но для демонстрации работы наших функций этого достаточно).
Чтобы эффективно манипулировать таким массивом и использовать данные внутри, нам как раз очень пригодятся filter , map и reduce .
I. Filter
Функция filter самая простая и понятная из великолепной тройки. Она проходится по массиву и отбирает только те элементы, которые подходят под заданное условие. А те, которые не подходят, соответственно, игнорирует.
Допустим, мы хотим выбрать из нашей коллекции только те комментарии, в которых упоминается console.log: в конце концов, отладка — это самый важный инструмент для понимания происходящего в коде, наверняка такой совет поступит не единожды.
Что здесь произошло? В функцию filter мы передали callback — по большому счету, просто функцию, аргументом которой является наш элемент коллекции — комментарий пользователя.
Для каждого элемента коллекции мы выбрали интересующую нас деталь, а именно, текст комментария comment.text , и проверили, содержит ли текст подстроку «console.log». Если это так, callback вернет true , и весь наш объект-комментарий будет добавлен в результат. В противном случае, callback вернет false , и результат не изменится.
Вот, кстати, и он:
Мы успешно отфильтровали комментарии по условию, дело сделано!
Рекомендую вам самостоятельно потестировать этот код на repl.it, чтобы убедиться, что все работает именно так. Последуйте совету Ивана Редьюсова — используйте отладку и посмотрите, как добавляются элементы 😉
II. Map
Функция map немного сложнее. Для каждого обработанного элемента коллекции она добавит в результат один элемент, измененный так, как мы укажем в callback функции.
В этом примере мы даже не изменили, а заменили элементы. Функция вернула 5 на каждый элемент изначального массива. Практического смысла в этом немного, но сам механизм вы должны понимать — мы могли бы вставить вместо числа массив или объект, и функция map также заполнила бы результирующий массив указанными сущностями в соотношении 1:1 (один элемент изначального массива — один элемент конечного массива, вне зависимости от его внутренней сложности).
Вернемся к нашему массиву комментариев. Допустим, мы хотим получить коллекцию имен всех пользователей, которые отписались в вашем топике:
Отображение сработало как надо, и мы получили интересующие нас детали. Попробуйте самостоятельно извлечь из комментариев другие элементы. Измените их прямо в callback функции — к примеру, извлеките рейтинг пользователей и переведите его в двоичную систему счисления!
III. Reduce
Функция reduce , наверное, самая сложная из нашей тройки, ведь, помимо элементов коллекции, в ней появляется аккумулятор, с которым нужно научиться правильно работать. Эта функция производит «свертку», то есть, берет элементы из коллекции и из их множества создает какую-то одну новую сущность. Например, из массива — объект или число.
Технически функция reduce может заменить и filter , и map , но это, скорее всего, введет в заблуждение ваших коллег-программистов, поэтому старайтесь применять каждую функцию по прямому назначению. Для фильтрации — фильтрацию, для свертки — свертку.
Теперь попробуем применить функцию reduce . Предположим, мы хотим, имея нашу коллекцию комментариев, создать объект, в котором ключами будут имена пользователей, а значениями — все комментарии данного пользователя.
Давайте пошагово разберем, что здесь происходит.
Из примечательного — у нас появляется аккумулятор. Это переменная, в которую мы будем складывать промежуточные результаты. Вспомните — функции высшего порядка обрабатывают элемент за элементом, а на выходе из reduce у нас должна получиться некая новая сущность. Ее мы будем наполнять последовательно, так что без аккумулятора не обойтись. В нашем случае переменная называется acc , но вы, конечно, можете придумать любое другое имя.
Во-вторых, у нас появляется инициализатор типа, к которому мы сводим коллекцию. В нашем случае это объект, поэтому ставим <> . На этом месте мог бы быть массив [] , строка '' или число.
Теперь давайте разберемся с внутренней логикой callback функции.
В нашем примере возможны два сценария: в объекте уже есть ключ (имя пользователя), и тогда мы должны добавить в значения новый комментарий; или в объекте еще нет такого ключа, и тогда нам нужно его создать.
Что происходит здесь? Мы проверяем, есть ли в аккумуляторе искомый ключ. Для этого используем функцию _.has() из библиотеки Lodash. Если ключ есть, то добавляем текст комментария в значение ключа (оно представлено массивом).
И возвращаем аккумулятор — это важно!
Если же ключа нет, тогда добавляем его в наш результирующий объект, и передаем ему значение — массив с одним элементом, то есть, первым найденным нами комментарием этого пользователя. А с помощью spread-оператора мы копируем в объект всю накопленную аккумулятором информацию. Не забываем про возврат.
И вот он, долгожданный результат:
Как видите, из массива со множеством элементов мы создали один-единственный объект, зато наполнили его нужным нам содержимым. Попробуйте применить другую логику, например, вместо имени, используйте рейтинг пользователя.
IV. Бонус — функция forEach и особый синтаксис
Среди функций высшего порядка есть еще один любопытный экземпляр, функция forEach. Она используется для перебора элементов массива прямо как цикл for. of. Но, в отличие от цикла, более гибко встраивается в синтаксис функций высшего порядка.
Особенность этого синтаксиса в том, что функции высшего порядка можно запускать последовательно, как методы, не создавая промежуточных констант:
Не стану подробно расписывать этот пример, думаю, теперь вы можете провернуть такую операцию самостоятельно!
Заключение
Спасибо, что прочитали эту статью! Надеюсь, функции высшего порядка стали вам немного понятнее и ближе. Практикуйтесь, и они станут вашими лучшими помощниками.
Метод массивов filter() в JavaScript
Метод массивов filter() создает новый массив из элементов существующего массива, которые отвечают заданным критериям:
var numbers = [1, 3, 6, 8, 11];
var lucky = numbers.filter(function(number) <
return number > 7;
>);
// [ 8, 11 ]
В приведенном выше примере используется массив numbers. В результате возвращается новый фильтрованный массив, содержащий только те значения, которые больше 7.
Синтаксис метода filter()
var newArray = array.filter(function(item) <
return condition;
>);
Аргумент item – это ссылка на текущий элемент в массиве, поскольку filter() проверяет его на соответствие условию. В случае объектов это позволяет получить доступ к свойствам.
Если текущий элемент соответствует условию, он отправляется в новый массив.
Фильтрация массива объектов
Типичный вариант использования метода .filter() – это фильтрация массива объектов по их свойствам:
Дополнительные ресурсы
Дополнительную информацию о методе filter() вы найдете на MDN.
Метод filter() – это лишь один из нескольких методов итерации массивов в JavaScript.
Array.prototype.filter()
Метод filter() создаёт новый массив со всеми элементами, прошедшими проверку, задаваемую в передаваемой функции.
Интерактивный пример
Синтаксис
Параметры
Функция-предикат, которая будет вызвана для проверки каждого элемента массива. Если функция возвращает true , то элемент остаётся в массиве, если false , то удаляется.
Принимает три аргумента
Текущий обрабатываемый элемент в массиве.
Индекс текущего обрабатываемого элемента в массиве.
Обрабатываемый массив, на котором был вызван метод filter() .
Значение, используемое в качестве this при вызове колбэк-функции callbackFn .
Возвращаемое значение
Вернётся новый массив с элементами, которые прошли проверку. Если ни один элемент не прошёл проверку, то будет возвращён пустой массив.
Описание
Метод filter() вызывает переданную функцию callback один раз для каждого элемента, присутствующего в массиве, и создаёт новый массив со всеми значениями, для которых функция callback вернула значение, которое может быть приведено к true . Функция callback вызывается только для индексов массива с уже определёнными значениями; она не вызывается для индексов, которые были удалены или которым значения никогда не присваивались. Элементы массива, не прошедшие проверку функцией callback , просто пропускаются и не включаются в новый массив.
Функция callback вызывается с тремя аргументами:
- значение элемента;
- индекс элемента;
- массив, по которому осуществляется проход.
Если в метод filter() был передан параметр thisArg , при вызове callback он будет использоваться в качестве значения this . В противном случае в качестве значения this будет использоваться значение undefined . В конечном итоге, значение this , наблюдаемое из функции callback , определяется согласно обычным правилам определения this .
Метод filter() не изменяет массив, для которого он был вызван.
Элементы массива, обрабатываемые методом filter() , устанавливается до первого вызова функции callback . Элементы, добавленные в массив после начала выполнения метода filter() , либо изменённые в процессе выполнения, не будут обработаны функцией callback . Соответствующим образом, если существующие элементы удаляются из массива, они также не будут обработаны
Предупреждение: одновременное изменение элементов, описанное в предыдущем параграфе, часто приводит к труднопонимаемому коду, поэтому не рекомендуется делать это (за исключением особых случаев).
Примеры
Фильтрация всех маленьких значений
Следующий пример использует filter() для создания отфильтрованного массива, все элементы которого больше или равны 10, а все меньшие 10 удалены.
Найти все простые числа в массиве
Следующий пример возвращает все простые числа в массиве:
Фильтрация неверных записей в JSON
В следующем примере метод filter() используется для создания отфильтрованного JSON-объекта, все элементы которого содержат ненулевое числовое поле id .
Поиск в массиве
В следующем примере filter() используется для фильтрации содержимого массива на основе входных данных.
Реализация с использованием ES2015
Модификация изначального массива (изменение, добавление и удаление)
В следующих примерах проверяется поведение метода filter при изменении массива.
How to filter object array based on attributes?
I have the following JavaScript array of real estate home objects:
What I would like to do is be able to perform a filter on the object to return a subset of «home» objects.
For example, I want to be able to filter based on: price , sqft , num_of_beds , and num_of_baths .
How can I perform something in JavaScript like the pseudo-code below:
Note, the syntax does not have to be exactly like above. This is just an example.
21 Answers 21
This method is part of the new ECMAScript 5th Edition standard, and can be found on almost all modern browsers.
For IE, you can include the following method for compatibility:
![]()
I’m surprised no one has posted the one-line response:
. and just so you can read it easier:
![]()
You can try using framework like jLinq — following is a code sample of using jLinq
For more information you can follow the link http://www.hugoware.net/projects/jlinq
I prefer the Underscore framework. It suggests many useful operations with objects. Your task:
can be overwriten like:
Hope it will be useful for you!
here is the working fiddle which works fine in IE8 using jquery MAP function
![]()
You can use jQuery.grep() since jQuery 1.0:
You could do this pretty easily — there are probably many implementations you can choose from, but this is my basic idea (and there is probably some format where you can iterate over an object with jQuery, I just cant think of it right now):
And then you could invoke this function like so:
This way, you can invoke the filter based on whatever predicate you define, or even filter multiple times using smaller filters.
I use my ruleOut function for filtering objects based on specific unwanted property values. I understand that in your example you would like to use conditions instead of values, but my answer is valid for the question title, so I’d like to leave my method here.
Say you have a list of actors like this:
and you would like to find all actors that are rated as Holywood stars, their nationality should not be one of ‘English’, ‘Italian’, ‘Spanish’, ‘Greek’, plus their name would not be one of ‘Mary’, ‘Joe’. Bizzar example, I know! Anyway, with that set of conditions you would create the following object:
OK, now if you ruleOut(actors, unwantedFieldsFilter) you would only get
And Bill is your man, since his name is not one of ‘Mary’, ‘Joe’, his nationality is not included in [‘English’, ‘Italian’, ‘Spanish’, ‘Greek’] plus he is a Star!
There is one option in my method, that is applyAllFilters and is true by default. If you would try to ruleOut with this param set as false, that would work as an ‘OR’ filtering instead of ‘AND’. Example: ruleOut(actors,