Lab / JavaScript

JavaScript: Понимание функции Filter()

В этой статье мы поговорим о том как использовать функцию Array.prototype.filter() для фильтрации данных и получения выборки по заданным параметрам. Это продолжение серии мини-курса по работе с данными в JavaScript.

Содержимое мини-курса:

Определение и синтаксис

Метод filter() возвращает новый массив, созданный из всех элементов, которые проходят определенные условия фильтрации, предварительно сформированные в исходном массиве.

Вот как выглядит синтаксис:

let newArr = oldArr.filter(callback);

Наша функция обратного вызова может принимать три аргумента:

Filter vs. For Loop

Вы можете предстваить себе метод работы функции filter() на примере цикла for, который специально предназначен для для создания условий проверки значений массива, перебирая их построчно. Давайте рассмотрим на примере следующего кода:

let arr = [1, 2, 3, 4, 5, 6];

let even = [];

for(var i = 0; i < arr.length; i++) {
  if (arr[i] % 2 === 0) even.push(arr[i]);
}

// even = [2,4,6]

Этот код перебирает все значения массива arr и затем при помощи условия if (arr[i] % 2 === 0) even.push(arr[i]) записывает прошедшие проверку результаты в новый массив even. Результат представляет собой массив всех четных чисел: even = [2,4,6] тех что по условию поделились на два без остатка.

Этот код конечно же прекрасно справляеться со своей задачей, но есть более простой способ добиться того же результата — с помощью функции filter().

Чтобы наконец начать пользоваться функцией filter(), давайте используем тот-же самый пример с поиском чётных чисел в массиве.

Снова используем массив arr со списком доступных значений. Так как мы хотим получить только четные числа, будем возвращать true, если число четное, и false, если число нечетное. Вот один из способов добиться желаемого результата:

let arr = [1,2,3,4,5,6];

let even = arr.filter(val => {
  return val % 2 === 0;
});

// even = [2,4,6]

Вот так вот просто! Больше нет необходимости перебирать условия в цикле, и пушить их в новый массив на каждой итерации. Используя функцию filter() все, что вам нужно сделать — задать условия, а затем вернуть значение true для жлементов соответствующих условиям фильтрации.

На примере кода выше мы снова сделали проверку делиться ли число на два без отстатка, если условие выполнено, возвращаем true таким образом мы получаем очень простую и декларативную фильтрацию по чётным числам.

Пример 2

Давайте немного усложним задачу, выполним фильтрацию на примере массива объектов. Вот данные с которыми мы будем работать:

let data = [
  {
    country: 'China',
    population: 1409517397,
  },
  {
    country: 'India',
    population: 1339180127,
  },
  {
    country: 'USA',
    population: 324459463,
  },
  {
    country: 'Indonesia',
    population: 263991379,
  }
]

Наша задача: получить выборку стран население которых привышает 500 миллионов человек.

Для того, что бы добиться желаемого результата, выполним проверку где data.population выше 500,000,000. И в случае прохождения условия вернём значение true.

let cities = data.filter(val => {
  return val.population > 500000000;
});

В результате мы получим:

// cities = [{country: "China", population: 1409517397},
             {country: "India", population: 1339180127}]

Вот так вот просто всего в две строки мы получаем выборку стран с населением более 500 миллионов жителей. Несмотря на то, то мы усложнили входные данные, сам процесс остался фильтрации практически не изменился от предыдущего примера, где мы искали чётные числа.

Упрощаем с ES6

Несмотря на то, что мы написали очень которко и декларативно нашу функцию фильтрации, есть спсоб ещё больше её укоротить, в этом нам помогут стрелочные функции пришедшие в спецификации JavaScript ES6

let cities = data.filter(val => val.population > 500000000);

Этот код абсолютно идентичен примеру выше и аналогично проверяет каждое значение в нашем массиве, чтобы определить, превышает ли население стран отметку 500 миллионов жителей.

Давайте закрепим полученные знания, и сделаем выборку продуктов попадающую в промежуток заданных цен:

  let dataProducts = [
    {
      product: 'Apple',
      price: 10,
    },
    {
      product: 'Orange',
      price: 13,
    },
    {
      product: 'Egg',
      price: 40,
    },
    {
      product: 'Bread',
      price: 6,
    }
  ];

  let products = dataProducts.filter(val => {
    return val.price >= 10 && val.price <= 15;
  });

Вот это уже больше похоже на реальное примерение не так-ли? Таким образом можно организовываьт выборку товаров в магазине в рамках заданной цены.

На практике-же может оказаться что этих знаний недостаточно, так как данные часто имеют более сложную структуру, ветвления и вложенности. Давайте же укротим подобные данные!

const Resources = [
  {
    title: 'Learn JS',
    categories: [
      {
        name: 'javascript'
      },
      {
        name: 'css'
      }
    ]
  },
  {
    title: 'Learn CSS',
    categories: [
      {
        name: 'css'
      }
    ]
  },
  {
    title: 'Learn other stuff',
    categories: [
      {
        name: 'jQuery'
      },
      {
        name: 'javascript'
      }
    ]
  },
  {
    title: 'Learn node',
    categories: [
      {
        name: 'node'
      }
    ]
  },
  {
    title: 'Learn React',
    categories: [
      {
        name: 'react'
      }
    ]
  },
];

Как вы видите у нас тут появились категории, которые могут иметь вложенность и несколько значений. Для таких случаев у функции filter() есть метод some. Давайте воспользуемся им:

function getResourcesByCategoryName(Resources, CategoryName){

      return Resources.filter(function(resource){
               return resource
                      .categories
                      .some(function(category){ return category.name == CategoryName; });
             });
}

// Вызываем функуию фильтрации, 
// первым параметром передаём нашу коллекцию, 
//вторым параметром имя категории "node"

let Result = getResourcesByCategoryName(Resources, 'node');

console.log(Result);

Ну как вам такой заход? Теперь то у вас точно не должно возникнуть никаких проблем с применением метода Array.filter() в реальном проекте!