Подробно про метод filter() в javascript

Методы pop/push, shift/unshift

Очередь – один из самых распространённых вариантов применения массива. В области компьютерных наук так называется упорядоченная коллекция элементов, поддерживающая два вида операций:

  • добавляет элемент в конец.
  • удаляет элемент в начале, сдвигая очередь, так что второй элемент становится первым.

Массивы поддерживают обе операции.

На практике необходимость в этом возникает очень часто. Например, очередь сообщений, которые надо показать на экране.

Существует и другой вариант применения для массивов – структура данных, называемая стек.

Она поддерживает два вида операций:

  • добавляет элемент в конец.
  • удаляет последний элемент.

Таким образом, новые элементы всегда добавляются или удаляются из «конца».

Примером стека обычно служит колода карт: новые карты кладутся наверх и берутся тоже сверху:

Массивы в JavaScript могут работать и как очередь, и как стек. Мы можем добавлять/удалять элементы как в начало, так и в конец массива.

В компьютерных науках структура данных, делающая это возможным, называется двусторонняя очередь.

Методы, работающие с концом массива:

Удаляет последний элемент из массива и возвращает его:

Добавляет элемент в конец массива:

Вызов равнозначен .

Методы, работающие с началом массива:

Удаляет из массива первый элемент и возвращает его:

Добавляет элемент в начало массива:

Методы и могут добавлять сразу несколько элементов:

More JavaScript Array filter() method examples

Because the method returns an a new array, you can chain the result with other iterative methods such as and .

For example, the following illustrates how to chain the three methods: and

Output:

How it works.

  • First, the method returns the cities whose populations are less than 3 million.
  • Second, the method sorts the resulting cities by the populations in descending order
  • Third, the method show each element in the result array in the web console.

The following example illustrates the use of the argument that specifies an object which can be referenced in the function using the keyword.

Output:

How it works.

  • First, define the function that checks if its argument is a number and in the range specified by the and properties of an object.
  • Next, define an array of mixed data that contains , , and .
  • Then, define the object with two properties and .
  • After that, call the methods of the array and pass in the function and the object. Because we pass in the object, inside the function, the keyword references to the object.
  • Finally, show the result array in the web console.

In this tutorial, you have learned how to use the JavaScript Array method to filter elements in an array based on a test provided by a callback function.

  • Was this tutorial helpful ?

Array.isArray

Массивы не
образуют отдельный тип языка. Они основаны на объектах. Поэтому typeof не может
отличить простой объект от массива:

console.log(typeof {}); // object
console.log (typeof ); // тоже object

Но массивы
используются настолько часто, что для этого придумали специальный метод: Array.isArray(value). Он возвращает
true, если value массив, и false, если нет.

console.log(Array.isArray({})); // false
console.log(Array.isArray()); // true

Подведем итоги
по рассмотренным методам массивов. У нас получился следующий список:

Для
добавления/удаления элементов

push(…items)

добавляет элементы в конец

pop()

извлекает элемент с конца

shift()

извлекает элемент с начала

unshift(…items)

добавляет элементы в начало

splice(pos, deleteCount, …items)

начиная с индекса pos, удаляет
deleteCount элементов и вставляет items

slice(start, end)

создаёт новый массив, копируя в него
элементы с позиции start до end (не включая end)

concat(…items)

возвращает новый массив: копирует все
члены текущего массива и добавляет к нему items (если какой-то из items
является массивом, тогда берутся его элементы)

Для поиска
среди элементов

indexOf/lastIndexOf(item, pos)

ищет item, начиная с позиции pos, и
возвращает его индекс или -1, если ничего не найдено

includes(value)

возвращает true, если в массиве
имеется элемент value, в противном случае false

find/filter(func)

фильтрует элементы через функцию и
отдаёт первое/все значения, при прохождении которых через функцию
возвращается true

findIndex(func)

похож на find, но возвращает индекс
вместо значения

Для перебора
элементов

forEach(func)

вызывает func для каждого элемента.
Ничего не возвращает

Для
преобразования массива

map(func)

создаёт новый массив из результатов
вызова func для каждого элемента

sort(func)

сортирует массив «на месте», а потом
возвращает его

reverse()

«на месте» меняет порядок следования
элементов на противоположный и возвращает изменённый массив

split/join

преобразует строку в массив и обратно

reduce(func, initial)

вычисляет одно значение на основе
всего массива, вызывая func для каждого элемента и передавая промежуточный
результат между вызовами

Видео по теме

JavaScipt #1: что это такое, с чего начать, как внедрять и запускать

JavaScipt #2: способы объявления переменных и констант в стандарте ES6+

JavaScript #3: примитивные типы number, string, Infinity, NaN, boolean, null, undefined, Symbol

JavaScript #4: приведение типов, оператор присваивания, функции alert, prompt, confirm

JavaScript #5: арифметические операции: +, -, *, /, **, %, ++, —

JavaScript #6: условные операторы if и switch, сравнение строк, строгое сравнение

JavaScript #7: операторы циклов for, while, do while, операторы break и continue

JavaScript #8: объявление функций по Function Declaration, аргументы по умолчанию

JavaScript #9: функции по Function Expression, анонимные функции, callback-функции

JavaScript #10: анонимные и стрелочные функции, функциональное выражение

JavaScript #11: объекты, цикл for in

JavaScript #12: методы объектов, ключевое слово this

JavaScript #13: клонирование объектов, функции конструкторы

JavaScript #14: массивы (array), методы push, pop, shift, unshift, многомерные массивы

JavaScript #15: методы массивов: splice, slice, indexOf, find, filter, forEach, sort, split, join

JavaScript #16: числовые методы toString, floor, ceil, round, random, parseInt и другие

JavaScript #17: методы строк — length, toLowerCase, indexOf, includes, startsWith, slice, substring

JavaScript #18: коллекции Map и Set

JavaScript #19: деструктурирующее присваивание

JavaScript #20: рекурсивные функции, остаточные аргументы, оператор расширения

JavaScript #21: замыкания, лексическое окружение, вложенные функции

JavaScript #22: свойства name, length и методы call, apply, bind функций

JavaScript #23: создание функций (new Function), функции setTimeout, setInterval и clearInterval

reduce/reduceRight

Метод «arr.reduce(callback)» используется для последовательной обработки каждого элемента массива с сохранением промежуточного результата.

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

Метод используется для вычисления на основе массива какого-либо единого значения, иначе говорят «для свёртки массива». Чуть далее мы разберём пример для вычисления суммы.

Он применяет функцию по очереди к каждому элементу массива слева направо, сохраняя при этом промежуточный результат.

Аргументы функции :

  • – последний результат вызова функции, он же «промежуточный результат».
  • – текущий элемент массива, элементы перебираются по очереди слева-направо.
  • – номер текущего элемента.
  • – обрабатываемый массив.

Кроме , методу можно передать «начальное значение» – аргумент . Если он есть, то на первом вызове значение будет равно , а если у нет второго аргумента, то оно равно первому элементу массива, а перебор начинается со второго.

Проще всего понять работу метода на примере.

Например, в качестве «свёртки» мы хотим получить сумму всех элементов массива.

Вот решение в одну строку:

Разберём, что в нём происходит.

При первом запуске – исходное значение, с которого начинаются вычисления, равно нулю (второй аргумент ).

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

Поток вычислений получается такой

В виде таблицы где каждая строка – вызов функции на очередном элементе массива:

результат
первый вызов
второй вызов
третий вызов
четвёртый вызов
пятый вызов

Как видно, результат предыдущего вызова передаётся в первый аргумент следующего.

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

Посмотрим, что будет, если не указать в вызове :

Результат – точно такой же! Это потому, что при отсутствии в качестве первого значения берётся первый элемент массива, а перебор стартует со второго.

Таблица вычислений будет такая же, за вычетом первой строки.

Метод arr.reduceRight работает аналогично, но идёт по массиву справа-налево.

Функции-«колбэки»

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

Давайте напишем функцию с тремя параметрами:

Текст вопроса
Функция, которая будет вызываться, если ответ будет «Yes»
Функция, которая будет вызываться, если ответ будет «No»

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

На практике подобные функции очень полезны. Основное отличие «реальной» функции от примера выше будет в том, что она использует более сложные способы взаимодействия с пользователем, чем простой вызов . В браузерах такие функции обычно отображают красивые диалоговые окна. Но это уже другая история.

Аргументы функции ещё называют функциями-колбэками или просто колбэками.

Ключевая идея в том, что мы передаём функцию и ожидаем, что она вызовется обратно (от англ. «call back» – обратный вызов) когда-нибудь позже, если это будет необходимо. В нашем случае, становится колбэком’ для ответа «yes», а – для ответа «no».

Мы можем переписать этот пример значительно короче, используя Function Expression:

Здесь функции объявляются прямо внутри вызова . У них нет имён, поэтому они называются анонимными. Такие функции недоступны снаружи (потому что они не присвоены переменным), но это как раз то, что нам нужно.

Подобный код, появившийся в нашем скрипте выглядит очень естественно, в духе JavaScript.

Функция – это значение, представляющее «действие»

Обычные значения, такие как строки или числа представляют собой данные.

Функции, с другой стороны, можно воспринимать как «действия».

Мы можем передавать их из переменной в переменную и запускать, когда захотим.

splice

Метод splice() – это
универсальный «швейцарский нож» для работы с массивами. Умеет всё: добавлять,
удалять и заменять элементы. Его синтаксис такой:

Array.splice(index)

Он начинает с
позиции index, удаляет deleteCount элементов и
вставляет elem1, …, elemN на их место.
Возвращает массив из удалённых элементов. Этот метод проще всего понять,
рассмотрев примеры. Начнем с удаления. Предположим, имеется массив:

let ar = "Я", "смотрю", "этот", "обучающий", "урок";

Удалим 3-й и 4-й
элементы «этот» и «обучающий»:

ar.splice(2, 2);

мы здесь указали
индекс элемента, с которого происходит удаление и число удаляемых элементов.
Выведем результат в консоль:

console.log(ar);

Видим, в массиве
остались строки «я», «смотрю», «урок».

В следующем
примере мы удалим первые три элемента и добавим два других:

let delElem = ar.splice(, 3, "Это", "классный");

получаем массив ar:

«Это»,
«классный», «обучающий», «урок»

и массив delElem, состоящий из
удаленных элементов. Этот пример также показывает, что метод splice возвращает
массив из удаленных величин.

С помощью метода
splice можно вставлять
элементы, не удаляя существующие. Для этого аргумент deleteCount
устанавливается в 0:

ar.splice(3, , "интересный");

Получим массив:

«Я»,
«смотрю», «этот», «интересный», «обучающий»,
«урок»

В этом и в
других методах массива допускается использование отрицательного индекса. Он
позволяет начать отсчёт элементов с конца:

ar.splice(-3, 3, "это", "обучающее", "видео");

Здесь удаляются
последние 3 элемента и вместо них вставляются новые строчки.

JavaScript Array filter() method in detail

The following illustrates the syntax of the method:

The method creates a new array with all the elements that pass the test implemented by the function.

Internally, the method iterates over each element of the array and pass each element to the  function. If the  function returns , it includes the element in the return array.

The method accepts two named arguments: a  function and an optional object.

Like other iterative methods of the Array object such as and the  function has the following form:

The function takes three arguments:

  • The argument is the current element in the array that is being processed by the function.
  • The of the that is being processed by the function.
  • The object being traversed.

The and arguments are optional.

The argument of the method is optional. If you pass the value, you canreference it by using keyword inside the  function.

It is important to note that the method does not change the original array.

А. Перебор настоящих массивов

Для этого используются:
1. Известный метод Array.prototype.forEach.
2. Классический цикл for.
3. «Правильно» построенный цикл for…in.

Что же, давайте рассмотрим эти методы подробнее.

1. Метод forEach

Пример использования:

var a = "a", "b", "c"];
a.forEach(function(entry) {
    console.log(entry);
});

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

С помощью forEach вы не только сможете выполнить перебор всех элементов массива, но и получите возможность выполнения некоторых действий с массивами:
1) some — возвращает true, когда хотя бы для одного элемента массива колбэк возвращает значение, приводимое к true;
2) every — возвращает true, когда для каждого элемента массива колбэк возвращает значение, приводимое к true;
3) filter — обеспечивает создание нового массива, включающего те элементы исходного, для коих колбэк возвращает true;
4) reduce — сводит массив к единственному значению, т. е. колбэк применяется по очереди к каждому элементу массива, начиная с 1-го (полезно при вычислении суммы элементов массива и прочих итоговых функций);
5) map — обеспечивает создание нового массива, состоящего из значений, которые возвращаются колбэком;
6) reduceRight — работает так же, как и reduce с той лишь разницей, что перебирает элементы в обратном порядке.

2. Цикл for

Что тут скажешь — старый добрый for…

var a = "a", "b", "c"];
var index;
for (index = ; index < a.length; ++index) {
    console.log(aindex]);
}

Кстати, когда длина массива неизменна в течение цикла, а цикл принадлежит критическому с точки зрения производительности участку кода (что маловероятно), подходит «более оптимальная» версия for с хранением длины массива:

var a = "a", "b", "c"];
var index, len;
for (index = , len = a.length; index < len; ++index) {
    console.log(aindex]);
}

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

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

var a = "a", "b", "c"];
var index;
for (index = a.length - 1; index >= ; --index) {
    console.log(aindex]);
}

Однако справедливости ради стоит отметить, что в современных движках JavaScript вышеописанные игры с оптимизацией мало что значат.

3. Правильное использование цикла for…in

Вообще, цикл for…in не предназначен для перебора массивов. Он перебирает не индексы нашего массива, а перечисляемые свойства объекта.

Однако, если нам нужен перебор разреженных массивов, цикл for…in может быть весьма полезным, если, разумеется, соблюдать меры предосторожности:

// a — разреженный массив
var a = [];
a = "a";
a10 = "b";
a10000 = "c";
for (var key in a) {
    if (a.hasOwnProperty(key) &&
        /^0$|^\d*$/.test(key) &&
        key <= 4294967294) {
        console.log(akey]);
    }
}

В вышеописанном примере на каждой циклической итерации осуществляются 2 проверки:
1) то, что массив имеет своё свойство с именем key (ненаследованное из его прототипа);
2) то, что key — это строка, содержащая десятичную запись целого числа, значение которого менее 4294967294.

Да, такие проверки могут отнять много времени, но если мы имеем дело с разреженным массивом, данный способ эффективнее обычного цикла for, т. к. в последнем случае перебираются лишь элементы, которые определены в массиве явно. Например в коде выше произойдёт всего 3 итерации (для индексов 0, 10 и 10000), в то время как при использовании классического for — 10001 итерация.

Кстати, код проверок можете оформить в виде отдельной функции:

function arrayHasOwnIndex(array, key) {
    return array.hasOwnProperty(key) && /^0$|^\d*$/.test(key) && key <= 4294967294;
}

В таком случае тело цикла существенно сократится:

for (key in a) {
    if (arrayHasOwnIndex(a, key)) {
        console.log(akey]);
    }
}

Вышеописанный код универсален, но вы можете использовать версию и короче. Формально она не совсем правильна, зато подходит практически для любых случаев:

for (key in a) {
    if (a.hasOwnProperty(key) && String(parseInt(key, 10)) === key) {
        console.log(akey]);
    }
}

Определение и применение

JavaScript метод .filter() позволяет создать новый массив, элементы которого соответствуют условию заданному в пререданной функции (для которых функция возвращает true). Элементы массива, которые не соответствуют условию в переданной функции (для которых функция возвращает false) пропускаются и не включаются в новый массив отфильтрованных элементов.

Обращаю Ваше внимание, что функция обратного вызова, переданная в качестве параметра метода .filter() не будет вызвана для удалённых, или пропущенных элементов массива. Метод .filter() не изменяет массив для которого он был вызван

Диапазон элементов, обрабатываемых с помощью метода .filter() устанавливается перед первым вызовом функции обратного вызова. Если элементы были добавлены к массиву после её вызова, то на таких элементах функция вызвана не будет.

Если значения существующих элементов массива изменяются в момент выполнения, то в качестве передаваемого значения функции будет значение в тот момент времени, когда метод .filter() посещает их. Если элементы удаляются до их посещения, то такие элементы посещены не будут.

Ассоциативный JavaScript-массив как объект

В виде ассоциативного массива мы можем использовать и объект.

Для создания пустого ассоциативного массива (объекта) нам подойдёт один из следующих вариантов:

// с помощью литерала объекта
var arr = {};
// с помощью стандартной функции-конструктора Object
var arr = new Object();
// с помощью Object.create
var arr = new Object.create(null);

Чтобы заполнить ассоциативный массив в момент его создания, поступаем следующим образом:

var myArray = { 
  "ключ1" "значение1"
 ,"ключ2" "значение2"
 , ... }

Теперь добавим в наш ассоциативный массив элемент (пару «ключ-значение»):

// добавляем в массив arr строку «текстовое значение», которое связано с ключом «key1»
arr"key1" = "текстовое значение"
// добавляем в массив число 22, которое связано с ключом «key2»
arr"key2" = 22;

Обратите внимание, что добавление элемента в JavaScript-массив выполнится лишь тогда, когда данного ключа в нём нет. Если ключ уже имеется, то выражение лишь поменяет значение уже существующего ключа

В роли значения ключа мы можем использовать любой тип данных, включая объекты.
Стоит добавить, что в JavaScript кроме записи с квадратными скобками мы можем использовать точку. Однако это доступно лишь для ключей, имена которых соответствуют правилам именования переменных.

arr.key1 = "текстовое значение"
arr.key2 = 22;

Чтобы получить значение элемента по ключу, подойдёт следующий синтаксис:

myArray"key1"];
myArray"key2"];
myArray.key1;
myArray.key2;

Чтобы получить число ключей (длину) ассоциативного массива, поступаем следующим образом:

var myArray = { "key1""value1", "key2""value2", "key3""value3"}
// 1 – получаем массив ключей посредством метода keys
// 2 - применяем свойство length, дабы узнать длину массива
Object.keys(myArray).length; // 3

Если надо удалить элемент из ассоциативного массива, применяем оператор delete.

delete myArray"key1"];

Когда нужно проверить, существует ли ключ в нашем ассоциативном массиве:

var myArray = {"key1""value1", "key2""value2" };  

// 1 способ (задействуем метод hasOwnProperty)
if (myArray.hasOwnProperty("key1")) {
  console.log("Ключ key1 есть!");
} else {
  console.log("Ключ key1 не существует!");
}

// 2 способ
if ("key1" in myArray) {
  console.log("Ключ key1 существует в массиве!");
} else {
  console.log("Ключ key1 не существует в массиве!");
}

Если нужно перебрать элементы ассоциативного массива, подойдёт цикл for…in:

// myArray — ассоциативный массив
for(key in myArray) {
  console.log(key + " = " + myArraykey]);
}

А чтобы преобразовать ассоциативный JavaScript-массив в JSON и назад, поступаем так:

// Ассоциативный массив (объект)
var myArr = {
  key1 "value1",
  key2 "value2",
  key3 "value3"
};

// в JSON
jsonStr = JSON.stringify(myArr);
// из JSON в ассоциативный массив 
arr = JSON.parse(jsonStr);

//получаем значение по ключу key1 (выводим в консоль)
console.log(arr.key1);

При написании статьи использовались материалы:
— «JavaScript — Ассоциативные массивы»;
— «Ассоциативный массив как объект»;
— «Настоящие ассоциативные массивы в JavaScript».

Значения параметров

Параметр Описание
function Функция обратного вызова, которая будет выполнена один раз для каждого элемента в массиве. Функция принимает следующие параметры:

  • currentValue — значение текущего элемента
  • index — индекс массива текущего элемента.
  • arr — массив, к которому принадлежит текущий элемент (по которому происходит проход).

Если в качестве параметра метода передается что-то, что не является объектом функции, то будет вызвано исключение TypeError. Обязательный параметр.

thisValue Объект, на который может ссылаться ключевое слово this внутри функции обратного вызова. Если параметр thisValue не используется, то в качестве значения this будет использоваться undefined (в конечном счете this будет зависеть от обычных правил контекста выполнения функции). Необязательный параметр.

contextObject

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

function isInRange(value) {  if (typeof value !== 'number') {  return false;  }  return value >= this.lower && value <= this.upper;}const data = ;const range = {lower: 1, upper: 10};const numberInRange = data.filter(isInRange, range);console.log(numberInRange); // 

Как это работает.

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

Далее, создаётся массив из различных типов данных, в который включены числа, строки и .

Потом создаём объект , с двумя свойствами и .

И после всего этого, вызывается метод на массиве , с колбэком и объектом . Так как мы передаём объект , как , то внутри функции , будет ссылаться на этот самый объект.

И под конец, в консоль выводится получившийся массив.

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

const girls = ;const filterValues = (name) => {    return girls.filter(data => {        return data.toLowerCase().indexOf(name.toLowerCase()) > -1;    });}console.log(filterValues('le'));

Тут мы ищем строку, в которой есть подстрока ‘le’, если условие удовлетворено, то строка попадёт в итоговый массив. Вот, что мы получим на выходе.

Зачастую метод используют для того, чтобы исключить из массива: пустые строки, и (да и не только, как было показано в примере выше)

const arr = ;const filteredArr = arr.filter(function(val) {	return !(val === "" || typeof val == "undefined" || val === null);});// filteredArr = 

Ну и конечно же, всеми любимый аргумент , который уберет все false значения из массива, включая , пустые строки и .

const arr = ;const trueOnly = arr.filter(Boolean);// trueOnly = 

И последний пример, который учитывает все три аргумента для колбэк функции в .

let kgd = ;let pupersuper = [];const modifiedWords = kgd.filter((word,index,array)=>{  array+=' superpuper'  pupersuper.push(array);  return word.length >= 11});pupersuper = ;modifiedWords = ;

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

Заключение

Метод был представлен в ECMAScript 5, так что он недоступен для использования в старых брауpерах, таких как IE8 и т.п. Но если вам обязательно нужно использовать его для работы в них, то тогда вам придется воспользоваться компилятором или каким-нибудь из общедоступных полифилов. Ну или вы можете применить схожий по функционалу метод из таких библиотек, как Underscore или lodash.

Но сейчас это уже уж очень экзотический случай, так как поддержка .

Определение и применение

JavaScript метод forEach() позволяет выполнить переданную функцию один раз для каждого элемента в массиве в порядке возрастания индекса.

Обращаю Ваше внимание, что функция обратного вызова, переданная в качестве параметра метода forEach() не будет вызвана для удалённых, или пропущенных элементов массива. Диапазон элементов, обрабатываемых с помощью метода forEach() устанавливается перед первым вызовом функции обратного вызова

Если элементы были добавлены к массиву после её вызова, то на таких элементах функция вызвана не будет

Диапазон элементов, обрабатываемых с помощью метода forEach() устанавливается перед первым вызовом функции обратного вызова. Если элементы были добавлены к массиву после её вызова, то на таких элементах функция вызвана не будет.

Если значения существующих элементов массива изменяются в момент выполнения, то в качестве передаваемого значения функции будет значение в тот момент времени, когда метод forEach() посещает их. Если элементы удаляются до их посещения, то такие элементы посещены не будут. Если элементы, которые уже посещены, удаляются во время прохода по массиву, то более поздние элементы будут пропущены.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector