Array.prototype.filter()

Метод filter() (фільтрувати) примірників Array створює поверхневу копію частини даного масиву, відфільтрованого до тих елементів даного масиву, що проходять перевірку, реалізовану в переданій функції.

Спробуйте його в дії

Синтаксис

filter(callbackFn)
filter(callbackFn, thisArg)

Параметри

callbackFn

Функція до виконання над кожним елементом у масиві. Вона повинна повертати істинне значення, щоб зберегти елемент у результівному масиві, а інакше – хибне. Ця функція викликається із наступними аргументами:

element

Поточний елемент масиву, що опрацьовується функцією.

index

Індекс елементу, що опрацьовується.

array

Початковий масив, на якому було викликано filter().

thisArg Необов'язкове

Значення для використання як this при виконанні callbackFn. Більше про це в ітеративних методах.

Повернене значення

Поверхнева копія переданого масиву, що містить лише ті елементи, котрі проходять перевірку. Якщо жоден елемент не проходить перевірку, то повертається порожній масив.

Опис

Метод filter() є ітеративним методом. Він викликає передану як аргумент функцію callbackFn один раз для кожного елемента масиву і збирає новий масив з усіх значень, для яких функція callbackFn повернула істинне значення. Ті елементи масиву, які не пройшли перевірку функцією callbackFn, не додаються у новий масив.

callbackFn закликається лише на тих індексах масиву, які мають присвоєні значення. Вона не закликається для порожніх комірок в розріджених масивах.

Метод filter() є копіювальним методом. Він не вносить змін до this, а натомість повертає поверхневу копію, котра містить ті самі елементи, що присутні у вихідному масиві (виключивши частину з них). Проте функція, передана як callbackFn, може змінювати масив. Хоча зверніть увагу, що довжина масиву запам'ятовується до моменту першого заклику callbackFn. Таким чином:

  • callbackFn не оброблятиме жодних елементів, доданих поза початковою довжиною масиву, відколи почався виклик filter().
  • Зміни за вже обробленими індексами не призводять до повторного заклику на них callbackFn.
  • Якщо наявний, іще не оброблений елемент масиву вже був змінений callbackFn, то його значення, передане в callbackFn, буде значенням на ту мить, коли такий елемент обробляється. Видалені елементи – не обробляються.

Застереження: Паралельні зміни такого роду, як описано вище, часто призводять до важкозрозумілого коду, і їх загалом краще уникати (окрім особливих випадків).

Поліфіл

Метод filter() було додано до стандарту ECMA-262 у його 5-й редакції. Тобто, він може бути наявним не у всіх реалізаціях цього стандарту.

Цю проблему можна обійти шляхом додавання наступного коду на початку скрипту. Фактично це дасть змогу використовувати filter() у реалізаціях ECMA-262, які не підтримують його нативно. Цей алгоритм цілком еквівалентний наведеному в 5-й редакції ECMA-262 за умови, що fn.call зводиться до початкового значення Function.prototype.bind() і що Array.prototype.push() містить своє початкове значення.

if (!Array.prototype.filter) {
  Array.prototype.filter = function (func, thisArg) {
    "use strict";
    if (!((typeof func === "Function" || typeof func === "function") && this))
      throw new TypeError();

    var len = this.length >>> 0,
      res = new Array(len), // заздалегідь створюємо новий масив
      t = this,
      c = 0,
      i = -1;

    var kValue;
    if (thisArg === undefined) {
      while (++i !== len) {
        // перевіряємо, чи масив має якесь значення за даним індексом
        if (i in this) {
          kValue = t[i]; // на випадок, якщо t міняється всередині функції зворотного виклику
          if (func(t[i], i, t)) {
            res[c++] = kValue;
          }
        }
      }
    } else {
      while (++i !== len) {
        // перевіряємо, чи масив має якесь значення за даним індексом
        if (i in this) {
          kValue = t[i];
          if (func.call(thisArg, t[i], i, t)) {
            res[c++] = kValue;
          }
        }
      }
    }

    res.length = c; // скорочуємо масив до його дійсного розміру
    return res;
  };
}

Метод filter() є узагальненим. Він лишень очікує, що значення this має властивість length, а також властивості з цілочисловими ключами.

Приклади

Відфільтровування малих значень

Наступний приклад за допомогою filter() створює масив відфільтрованих значень, до якого не потраплять числа, менші за 10.

function isBigEnough(value) {
  return value >= 10;
}

const filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
// відфільтрований результат має [12, 130, 44]

Знаходження всіх простих чисел у масиві

Наступний приклад повертає всі прості числа, які є в масиві:

const array = [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];

function isPrime(num) {
  for (let i = 2; num > i; i++) {
    if (num % i === 0) {
      return false;
    }
  }
  return num > 1;
}

console.log(array.filter(isPrime)); // [2, 3, 5, 7, 11, 13]

Відсіювання некоректних елементів із JSON

Наступний приклад за допомогою filter() створює відфільтрований набір елементів, що містять ненульовий числовий id, записаних об'єктною нотацією.

const arr = [
  { id: 15 },
  { id: -1 },
  { id: 0 },
  { id: 3 },
  { id: 12.2 },
  {},
  { id: null },
  { id: NaN },
  { id: "undefined" },
];

let invalidEntries = 0;

function filterByID(item) {
  if (Number.isFinite(item.id) && item.id !== 0) {
    return true;
  }
  invalidEntries++;
  return false;
}

const arrByID = arr.filter(filterByID);

console.log("Відфільтрований масив\n", arrByID);
// Відфільтрований масив
// [{ id: 15 }, { id: -1 }, { id: 3 }, { id: 12.2 }]

console.log("Кількість некоректних значень =", invalidEntries);
// Кількість некоректних значень = 5

Пошук у масиві

Наступний приклад за допомогою filter() фільтрує вміст масиву за певним критерієм пошуку.

const fruits = ["яблуко", "банан", "виноград", "манго", "апельсин"];

/**
 * Фільтрує елементи масиву за певним критерієм пошуку (запиту)
 */
function filterItems(arr, query) {
  return arr.filter((el) => el.toLowerCase().includes(query.toLowerCase()));
}

console.log(filterItems(fruits, "ан")); // ['банан', 'манго']
console.log(filterItems(fruits, "ин")); // ['виноград', 'апельсин']

Використання filter() на розріджених масивах

filter() пропустить порожні комірки.

console.log([1, , undefined].filter((x) => x === undefined)); // [undefined]
console.log([1, , undefined].filter((x) => x !== 2)); // [1, undefined]

Виклик filter() на об'єктах-немасивах

Метод filter() зчитує з this властивість length, а потім звертається до кожної властивості, чий ключ – невід'ємне ціле число, менше за length.

const arrayLike = {
  length: 3,
  0: "a",
  1: "b",
  2: "c",
  3: "a", // ігнорується filter(), оскільки length – 3
};
console.log(Array.prototype.filter.call(arrayLike, (x) => x <= "b")); // [ 'a', 'b' ]

Внесення змін до початкового масиву (зміна, додавання і видалення елементів)

Наступний приклад перевіряє поведінку методу filter під час внесення змін до масиву.

// Змінювання кожного слова
let words = ["спрей", "межа", "розкішний", "згуба", "еліта", "дарунок"];

const modifiedWords = words.filter((word, index, arr) => {
  arr[index + 1] += " екстра";
  return word.length < 6;
});

console.log(modifiedWords);
// Зверніть увагу, в масиві є три слова з довжиною до 6 літер, але оскільки всі наступні змінюються, в результаті отримуємо лише одне
// ["спрей"]

// Додавання нових слів
let words = ["спрей", "межа", "розкішний", "згуба", "еліта", "дарунок"];
const appendedWords = words.filter((word, index, arr) => {
  arr.push("новий");
  return word.length < 6;
});

console.log(appendedWords);
// Лише три слова проходять перевірку, хоча масив 'words' тепер має значно більше слів, що містять до 6 літер
// ["спрей", "межа", "згуба", "еліта"]

// Видалення слів
let words = ["спрей", "межа", "розкішний", "згуба", "еліта", "дарунок"];
const deleteWords = words.filter((word, index, arr) => {
  arr.pop();
  return word.length < 6;
});

console.log(deleteWords);
// Слово 'еліта' взагалі не потрапляє до масиву з результатами, позаяк воно викидається з `words` до того, як фільтр добереться до нього
// ["спрей" ,"межа"]

Специфікації

Сумісність із браузерами

desktop mobile server
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet Deno Node.js
filter
Chrome Full support 1
Edge Full support 12
Firefox Full support 1.5
Internet Explorer Full support 9
Opera Full support 9.5
Safari Full support 3
WebView Android Full support 37
Chrome Android Full support 18
Firefox for Android Full support 4
Opera Android Full support 10.1
Safari on iOS Full support 1
Samsung Internet Full support 1.0
Deno Full support 1.0
Node.js Full support 0.10.0

Дивіться також