javascript - видалити елемент масиву за умови


77

Мені було цікаво, як би я застосував метод у javascript, який видаляє всі елементи масиву, що очищають певну умову. (Бажано без використання jQuery)

Напр.

ar = [ 1, 2, 3, 4 ];
ar.removeIf( function(item, idx) {
    return item > 3;
});

Вищезазначене пройде кожен елемент масиву та видалить усі, що return trueвідповідають умові (у прикладі, елемент> 3).

Я тільки починаю з javascript і цікаво, чи знав хтось про короткий ефективний спосіб зробити це.

- оновлення -

Також було б чудово, якби умова могла працювати і на властивості об’єкта.

Напр.

ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
ar.removeIf( function(item, idx) {
    return item.str == "c";
});

Куди предмет буде вилучено, якщо item.str == "c"

- update2 -

Було б непогано, якби умови індексу також могли працювати.

Напр.

ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
ar.removeIf( function(item, idx) {
    return idx == 2;
});

Відповіді:


50

Ви можете додати свій власний метод, Arrayякий робить щось подібне, якщо filterне працює для вас.

Array.prototype.removeIf = function(callback) {
    var i = 0;
    while (i < this.length) {
        if (callback(this[i], i)) {
            this.splice(i, 1);
        }
        else {
            ++i;
        }
    }
};

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

Array.prototype.removeIf = function(callback) {
    var i = this.length;
    while (i--) {
        if (callback(this[i], i)) {
            this.splice(i, 1);
        }
    }
};

Це дозволяє уникнути необхідності навіть турбуватися про оновлення lengthабо вловлювання наступного елемента, оскільки ви працюєте вліво, а не вправо.


Дякую, чудово працює. Мені подобається, як це враховує зміни індексу шляхом зрощування під час ітерації.
dk123

4
Це трохи безладно. Спробуйте jsfiddle.net/n8JEy/4 - і ви не перейшли iдо зворотного виклику як другий параметр
Ян

@Ian Я, як правило, віддаю перевагу читабельності, але ваше рішення, безумовно, є більш ефективним. Я також додав індекс як другий параметр (просто ігнорував його спочатку, оскільки він не використовувався, і я припустив, що він йому не потрібен).
pickypg

@ dk123 Як новачок у JavaScript, ви можете вільно контролювати параметри таких зворотних викликів, як вважаєте за потрібне, і можете додавати до них все, що завгодно, поки інформація доступна (наприклад, ви можете додати поточну довжину масиву, використовуючи this.lengthяк третій параметр, якщо у вас був якийсь нахил).
pickypg

@pickypg Немає проблем! Це точно точно. Здається, я просто не бачив причини так розділяти логіку, але це явно добре. І "брудний" був неправильним терміном. Не знав про другий параметр, просто хотів його вказати :)
Ян

94

Ви можете використовувати метод фільтрування масиву .

Код буде виглядати так:

ar = [1, 2, 3, 4];
ar = ar.filter(item => !(item > 3));
console.log(ar) // [1, 2, 3]


47
Якщо перекласти !(item > 3)на людську мову, це буде item <= 3.
Miha_x64

5
Майте на увазі, що метод Array.filter створює новий масив. Якщо ви посилаєтесь на свій масив в іншій змінній, посилання згодом буде розбито: ar1 = [1, 2, 3, 4]; ar2 = ar1; ar1 = ar1.filter(item => !(item > 3)); console.log(ar1); console.log(ar2);
Атілла Баспінар,

37

Ви можете використовувати Array.filter(), що робить навпаки:

ar.filter(function(item, idx) {
    return item <= 3;
});

2
Як голова вгору, що не підтримується в IE8 або нижче. Ви також повинні заперечити оригінальний вираз, або він повинен бути <=.
pickypg

17
Чи не повертає Array.filter () новий масив із відфільтрованими елементами? Чи є щось, що просто видаляє елементи з масиву?
dk123

@ dk123: Там нічого не зроблено заздалегідь. Вам доведеться написати forцикл.,
Blender

Створення нового масиву з Array.filter, швидше за все, буде швидшим, ніж зрощення вручну з даного масиву.
Кейсі Чу

1
@Blender Це дуже цікаво. Клянусь, я нещодавно подивився щось на порівняння двох, і все було навпаки. Мій дурний коментар видалено. Дякуємо, що надали це для доказу :)
Ян


2

Я люблю подібні запитання і просто іншу версію від мене ... :)

Array.prototype.removeIf = function(expression) {
   var res = [];
    for(var idx=0; idx<this.length; idx++)
    {
      var currentItem = this[idx];
        if(!expression(currentItem))
        {
            res.push(currentItem);
        }
    }
    return res;
}

ar = [ 1, 2, 3, 4 ];
var result = ar.removeIf(expCallBack);


console.log(result);
function expCallBack(item)
{
    return item > 3;
}

1
Чи не могли б ви трохи змінити (або додати інший метод) свій код, щоб фактично видалити елементи, замість повернення нового масиву? Дякую за відповідь.
dk123

3
хто голосував за це так неправильно. це найкраща відповідь із усіх. перевірте тест за допомогою @Blender jsperf.com/splice-vs-filter. це принаймні вдвічі швидше і до 15 разів швидше в останніх версіях Firefox
парламент

2

просто напишіть наступний приклад, якщо умова може працювати і на властивості об'єкта

var ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
var newArray = [];
for (var i = 0, len = ar.length; i<len; i++) {
        if (ar[i].str == "b") 
        {newArray.push(ar[i]);};
 };
console.log(newArray);

Див. Приклад Live Example


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.