Как я могу хранить только элементы массива, соответствующие определенному условию?

У меня есть массив, и я хочу его фильтровать, чтобы включать только элементы, которые соответствуют определенному условию. Это можно сделать в JavaScript?

Некоторые примеры:

[1, 2, 3, 4, 5, 6, 7, 8] // I only want [2, 4, 6, 8], i.e. the even numbers

["This", "is", "an", "array", "with", "several", "strings", "making", "up", "a", "sentence."] // I only want words with 2 or fewer letters: ["is", "an", "up", "a"]

[true, false, 4, 0, "abc", "", "0"] // Only keep truthy values: [true, 4, "abc", "0"]

Ответ 1

Для этого вы можете использовать метод Array#filter(), введенный в ECMAScript5. Он поддерживается во всех браузерах, за исключением IE8 и ниже, и в древних версиях Firefox. Если по какой-либо причине вам необходимо поддерживать эти браузеры, вы можете использовать polyfill для этого метода.

filter() принимает в качестве первого аргумента функцию. Для каждого элемента массива вашей функции передаются три аргумента - значение текущего элемента, его индекс в массиве и сам массив. Если ваша функция возвращает true (или правное значение, например 1, "pizza" или 42), этот элемент будет включен в результат. В противном случае это не произойдет. filter() возвращает новый массив - исходный массив останется неизменным. Это означает, что вам нужно где-то сохранить значение, или оно будет потеряно.

Теперь в примерах из вопроса:

var myNumbersArray = [1, 2, 3, 4, 5, 6, 7, 8];
console.log(myNumbersArray.filter(function(num){
  return !(num % 2); // keep numbers divisible by 2
}));
console.log(myNumbersArray); // see - it hasn't changed!

var myStringArray = ["This", "is", "an", "array", "with", "several", "strings", "making", "up", "a", "sentence."];
console.log(myStringArray.filter(function(str){
  return str.length < 3; // keep strings with length < 3
}));
console.log(myStringArray);

var myBoolArray = [true, false, 4, 0, "abc", "", "0"];
console.log(myBoolArray.filter(Boolean));
// wow, look at that trick!
console.log(myBoolArray);

Ответ 2

Чтобы фильтровать записи, которые не являются строго массивами, и, следовательно, не имеют свойства .filter на их прототипе, но все еще повторяемы (например, document.getElementsByTagName), вы можете использовать

Array.prototype.filter.call(collection, function filterFunction(el, index, collection) {
    ... 
});

Или сокращенное

[].filter.call(collection, function filterFunction(el, index, collection) {
    ...
});

Что касается объектов, которые не являются итерабельными, но вы по-прежнему хотите фильтровать свойства и получать массив ключей, которые проходят фильтрацию, вы можете комбинировать с Object.keys следующим образом:

var obj = { one: 1, two: 2, three: 3, four: 4 };
var filtered = Object.keys(obj).filter(function(key) {
    return obj[key] % 2 === 0;
}); //filtered == ['two', 'four']

Затем вы можете создать новый объект, содержащий эти свойства:

var filteredObj = filtered.reduce(function(newObj, currentKey) {
    newObj[currentKey] = obj[currentKey]; //Add the original value to the new object
    return newObj; //Return the new object to continue iteration
}, {}) // Begin iteration with a blank object

//filteredObj is now { two: 2, four: 4 }

Вышеупомянутое может быть даже объединено в функцию!

function filterObject(obj, testCallback) {
    return Object.keys(obj).filter(function(key, index, array) {
        return testCallback(obj[key], index, array); //Call original filter but pass the property
    }).reduce(function(newObj, currentKey) {
        newObj[currentKey] = obj[currentKey]; //Add the original value to the new object
        return newObj; //Return the new object to continue iteration
    }, {}); // Begin iteration with a blank object
}

И используйте вот так:

var obj = { one: 1, two: 2, three: 3, four: 4 };
var filteredObj = filterObject(obj, function(el) { return el % 2 === 0 });

Ответ 3

Более краткий ответ, следующий за @Scimonster, с использованием синтаксиса ES6, будет выглядеть следующим образом:

 // even numbers
const even = [1, 2, 3, 4, 5, 6, 7, 8].filter(n => n%2 == 0);
// words with 2 or fewer letters
const words = ["This", "is", "an", "array", "with", "several", "strings", "making", "up", "a", "sentence."].filter(el => el.length <= 2);
// truable elements
const trues = [true, false, 4, 0, "abc", "", "0"].filter(v => v);

console.log(even);
console.log(words);
console.log(trues);