.filter()

Фильтры для воды удерживают вредные частицы, а фильтр JS — плохие значения.

Время чтения: 5 мин

Кратко

Скопировано

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

Пример

Скопировано
        
          
          const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]const evenOnly = nums.filter(function (n) {  const remainder = n % 2  return remainder === 0})
          const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const evenOnly = nums.filter(function (n) {
  const remainder = n % 2

  return remainder === 0
})

        
        
          
        
      

Результат будет [2, 4, 6, 8, 10].

        
          
          const languages = ["Java", "TypeScript", "C#", "JavaScript", "Julia"]const jLanguages = languages.filter(function (language) {  return language.startsWith("J")})
          const languages = ["Java", "TypeScript", "C#", "JavaScript", "Julia"]

const jLanguages = languages.filter(function (language) {
  return language.startsWith("J")
})

        
        
          
        
      

Результат будет ['Java', 'JavaScript', 'Julia'].

Интерактивный пример:

Открыть демо в новой вкладке

Как пишется

Скопировано

Аналогично методу .forEach(), методу .filter() необходимо передать аргументом функцию. Главное отличие — функция должна возвращать boolean, т. е. результатом должен быть true или false. Такие функции называют предикатами.

Это предикат, так как функция возвращает boolean-результат сравнения:

        
          
          function isPositive(num) {  return num > 0}
          function isPositive(num) {
  return num > 0
}

        
        
          
        
      

Это предикат, так как метод .includes() у строки возвращает boolean:

        
          
          function hasChar(str, char) {  return str.includes(char)}
          function hasChar(str, char) {
  return str.includes(char)
}

        
        
          
        
      

А вот это не предикат, ведь функция возвращает число, даже несмотря на то, что любое число в JavaScript (кроме 0) может быть приведено к true:

        
          
          function sum(a, b) {  return a + b}
          function sum(a, b) {
  return a + b
}

        
        
          
        
      

От результата выполнения функции зависит, попадёт ли элемент в итоговый массив:

  • true — элемент попадёт в итоговый массив.
  • false — не попадёт в итоговый массив.
        
          
          function predicate(num) {  if (num >= 5) {    return true  }  return false}const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]// передаём предикатnums.filter(predicate) // [5, 6, 7, 8, 9, 10]// Либо делаем короче и просто возвращаем результат сравненияnums.filter((num) => num >= 5) // [5, 6, 7, 8, 9, 10]
          function predicate(num) {
  if (num >= 5) {
    return true
  }

  return false
}

const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// передаём предикат
nums.filter(predicate) // [5, 6, 7, 8, 9, 10]

// Либо делаем короче и просто возвращаем результат сравнения
nums.filter((num) => num >= 5) // [5, 6, 7, 8, 9, 10]

        
        
          
        
      

Функция, которую мы передаём в метод .filter(), принимает три параметра:

  • item — элемент массива в текущей итерации;
  • index — индекс текущего элемента;
  • arr — сам массив, который мы перебираем.
        
          
          const languages = ["Java", "TypeScript", "C#", "JavaScript", "Julia"]languages.filter(function (item, index, arr) {  console.log("Текущий элемент " + item)  console.log("Индекс " + index)  console.log("Массив " + arr)  return index >= 3})
          const languages = ["Java", "TypeScript", "C#", "JavaScript", "Julia"]

languages.filter(function (item, index, arr) {
  console.log("Текущий элемент " + item)
  console.log("Индекс " + index)
  console.log("Массив " + arr)

  return index >= 3
})

        
        
          
        
      

💡 В новом массиве отфильтрованные элементы будут находиться в том же порядке, в котором они были в исходном массиве.

💡 .filter() возвращает новый массив, при этом исходный массив никак не изменится.

💡 Из-за того, что JavaScript имеет динамическую типизацию, то нам ничего не мешает возвращать какое угодно значение из функции. В этом случае JavaScript сам определит его истинность. Стоит помнить, что значения 0, undefined, null и пустая строка '' считаются ложными и равны false.

Truthy и falsy: Преобразование типов.

        
          
          const goods = [  {    name: "AirPods",    description: "Классные беспроводные наушники",  },  {    name: "MacBook Pro",    description: "Ноутбук на все случаи жизни",  },  {    name: "iPhone",    description: "",  },  {    name: "Дошик",  },]// Просто возвращаем значения описанияconst withDescription = goods.filter(function (item) {  return item.description})
          const goods = [
  {
    name: "AirPods",
    description: "Классные беспроводные наушники",
  },
  {
    name: "MacBook Pro",
    description: "Ноутбук на все случаи жизни",
  },
  {
    name: "iPhone",
    description: "",
  },
  {
    name: "Дошик",
  },
]

// Просто возвращаем значения описания
const withDescription = goods.filter(function (item) {
  return item.description
})

        
        
          
        
      

В результате получим массив с AirPods и MacBook Pro.

Для хорошей читаемости и понимания кода лучше всегда явно возвращать boolean-значение из функции-предиката.

💡 В JavaScript функция, в которой нет явного возвращаемого значения (т. е. нет return) все равно возвращает undefined. Потому, если забыть вернуть результат в функции в методе .filter(), то в результате получим пустой массив, так как отфильтруются все элементы.

        
          
          const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]const filtered = nums.filter(function (num) {  // Забыли вернуть результат  num >= 5})
          const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const filtered = nums.filter(function (num) {
  // Забыли вернуть результат
  num >= 5
})

        
        
          
        
      

Получим [], потому что undefined считается как false.

Как понять

Скопировано

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

Если решать такую задачу без .filter(), то выйдет так:

        
          
          const nums = [1, 2, 3, 4, 5, 6]const odds = []for (let i = 0; i < nums.length; i++) {  if (nums[i] % 2 !== 0) {    odds.push(nums[i])  }}console.log(odds)
          const nums = [1, 2, 3, 4, 5, 6]
const odds = []

for (let i = 0; i < nums.length; i++) {
  if (nums[i] % 2 !== 0) {
    odds.push(nums[i])
  }
}

console.log(odds)

        
        
          
        
      

Результат будет [1, 3, 5].

.filter() позволит сильно сократить код и сделать его понятнее:

        
          
          const nums = [1, 2, 3, 4, 5, 6]const odds = nums.filter(function (num) {  return num % 2 !== 0})console.log(odds)
          const nums = [1, 2, 3, 4, 5, 6]

const odds = nums.filter(function (num) {
  return num % 2 !== 0
})
console.log(odds)

        
        
          
        
      

Результат — [1, 3, 5].

На практике

Скопировано

Егор Огарков советует

Скопировано

🛠 Так как filter возвращает массив, то у полученного массива мы можем продолжать по цепочке вызывать другие методы массива.

        
          
          const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]const result = nums.filter(num => num >= 5).map(...).reduce(...)
          const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const result = nums.filter(num => num >= 5).map(...).reduce(...)

        
        
          
        
      

🛠 В filter в качестве функции можно передать конструктор Boolean. Таким образом можно легко и быстро отфильтровать все элементы, которые при приведении к boolean будут равны false.

        
          
          const num = 3const elements = [0, "", "one", "two", num === 3 && "three", null].filter(  Boolean)
          const num = 3

const elements = [0, "", "one", "two", num === 3 && "three", null].filter(
  Boolean
)

        
        
          
        
      

Результат — ['one', 'two', 'three'].