Видаліть усі елементи, що містяться в іншому масиві


222

Я шукаю ефективний спосіб видалити всі елементи з масиву javascript, якщо вони є в іншому масиві.

// If I have this array:
var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];

// and this one:
var toRemove = ['b', 'c', 'g'];

Я хочу працювати з myArray, щоб залишити його в такому стані: ['a', 'd', 'e', 'f']

За допомогою jQuery я використовую grep()і inArray(), який добре працює:

myArray = $.grep(myArray, function(value) {
    return $.inArray(value, toRemove) < 0;
});

Чи існує чистий javascript спосіб зробити це без циклу та сплайсингу?




1
можливий дублікат Чи можете ви видалити один масив з іншого в JavaScript або jquery - ви не можете приділити багато уваги пропозиціям, написаним під час написання питання
mplungjan

Незважаючи ні на що, це завжди буде мати циклі на певному рівні.
Blue Skies

Якщо ви справді хочете, щоб він був "ефективним", ви не будете використовувати такі методи функціонального типу .filter(). Замість цього ви будете використовувати forпетлі. Ви можете уникнути, .splice()якщо оригінальне замовлення не потрібно підтримувати. Або є способи зробити .splice()більш ефективними, якщо ви думаєте, що буде видалено багато предметів.
Blue Skies

Відповіді:


379

Використовуйте Array.filter()метод:

myArray = myArray.filter( function( el ) {
  return toRemove.indexOf( el ) < 0;
} );

Невелике вдосконалення, оскільки Array.includes()розширилася підтримка браузера :

myArray = myArray.filter( function( el ) {
  return !toRemove.includes( el );
} );

Наступне пристосування за допомогою стрілочних функцій :

myArray = myArray.filter( ( el ) => !toRemove.includes( el ) );

23
OP: Якщо ви використовуєте Underscore.js, це в .difference()основному це робить.
Білл Крісвелл

Тільки те, що я шукав. Дякую. @BillCriswell, я перевірю підкреслення.
Торкніться

1
@AlecRust Перетворення всіх елементів у toRemove()верхній регістр та зміна зворотного дзвінка з elу el.toUpperCase().
Сірко

5
або коротше:myArray = myArray.filter( el => !toRemove.includes( el ) );
538ROMEO

1
чи не це замовлення n ^ 2?
Фрейзер Кіркман

34

filterМетод повинен зробити трюк:

const myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
const toRemove = ['b', 'c', 'g'];

// ES5 syntax
const filteredArray = myArray.filter(function(x) { 
  return toRemove.indexOf(x) < 0;
});

Якщо ваш toRemoveмасив великий, такий тип пошуку може бути неефективним. Більш ефективно було б створити карту, щоб пошуки були, O(1)а не O(n).

const toRemoveMap = toRemove.reduce(
  function(memo, item) {
    memo[item] = memo[item] || true;
    return memo;
  },
  {} // initialize an empty object
);

const filteredArray = myArray.filter(function (x) {
  return toRemoveMap[x];
});

// or, if you want to use ES6-style arrow syntax:
const toRemoveMap = toRemove.reduce((memo, item) => ({
  ...memo,
  [item]: true
}), {});

const filteredArray = myArray.filter(x => toRemoveMap[x]);

24

Якщо ви використовуєте масив об'єктів. Тоді наведений нижче код повинен зробити магію, коли властивістю об'єкта будуть критерії видалення повторюваних елементів.

У наведеному нижче прикладі було видалено копії, порівнюючи найменування кожного елемента.

Спробуйте цей приклад. http://jsfiddle.net/deepak7641/zLj133rh/

var myArray = [
  {name: 'deepak', place: 'bangalore'}, 
  {name: 'chirag', place: 'bangalore'}, 
  {name: 'alok', place: 'berhampur'}, 
  {name: 'chandan', place: 'mumbai'}
];
var toRemove = [
  {name: 'deepak', place: 'bangalore'},
  {name: 'alok', place: 'berhampur'}
];

for( var i=myArray.length - 1; i>=0; i--){
 	for( var j=0; j<toRemove.length; j++){
 	    if(myArray[i] && (myArray[i].name === toRemove[j].name)){
    		myArray.splice(i, 1);
    	}
    }
}

alert(JSON.stringify(myArray));



11

Набори ECMAScript 6 можуть використовуватися для обчислення різних елементів двох масивів:

const myArray = new Set(['a', 'b', 'c', 'd', 'e', 'f', 'g']);
const toRemove = new Set(['b', 'c', 'g']);

const difference = new Set([...myArray].filter((x) => !toRemove.has(x)));

console.log(Array.from(difference)); // ["a", "d", "e", "f"]


8

Я щойно реалізований як:

Array.prototype.exclude = function(list){
        return this.filter(function(el){return list.indexOf(el)<0;})
}

Використовувати як:

myArray.exclude(toRemove);

1
Розширювати prototypesрідні Об'єкти, наприклад, не дуже добре Array. Це може мати довгостроковий конфлікт з майбутнім розвитком мови ( див. flattenВипадок )
MarcoL

6

Якщо ви не можете використовувати нові елементи ES5, filterя вважаю, що ви застрягли з двома петлями:

for( var i =myArray.length - 1; i>=0; i--){
  for( var j=0; j<toRemove.length; j++){
    if(myArray[i] === toRemove[j]){
      myArray.splice(i, 1);
    }
  }
}

filter is not "new HTML5 stuff"
goofballLogic

Я повинен був написати "речі ES5". Це не було доступно для ES3
MarcoL

6
var myArray = [
  {name: 'deepak', place: 'bangalore'}, 
  {name: 'chirag', place: 'bangalore'}, 
  {name: 'alok', place: 'berhampur'}, 
  {name: 'chandan', place: 'mumbai'}
];
var toRemove = [
  {name: 'deepak', place: 'bangalore'},
  {name: 'alok', place: 'berhampur'}
];



myArray = myArray.filter(ar => !toRemove.find(rm => (rm.name === ar.name && ar.place === rm.place) ))

Не хотіли б ви додати трохи пояснень на таке добре прийняте запитання?
гармоніка141

1
Я годинами шукав рішення проблеми і просто знайшов її, просто приголомшливо. Велике спасибі!
Tarvo Mäesepp


3

Ви можете використовувати _.differenceBy від lodash

const myArray = [
  {name: 'deepak', place: 'bangalore'}, 
  {name: 'chirag', place: 'bangalore'}, 
  {name: 'alok', place: 'berhampur'}, 
  {name: 'chandan', place: 'mumbai'}
];
const toRemove = [
  {name: 'deepak', place: 'bangalore'},
  {name: 'alok', place: 'berhampur'}
];
const sorted = _.differenceBy(myArray, toRemove, 'name');

Приклад коду тут: CodePen


Що робити, якщо атрибут вкладений всередині об'єкта? Щось тест, як у вашому випадку {name: 'deepak', місце: 'bangalore', вкладене: {test: 1}}
Charith Jayasanka

2

Як щодо найпростішого можливого:

var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
var toRemove = ['b', 'c', 'g'];

var myArray = myArray.filter((item) => !toRemove.includes(item));
console.log(myArray)


2
Майте на увазі, що includesце не доступно до ES7.
Грег

0

Правильний спосіб видалення всіх елементів, що містяться в іншому масиві, - це зробити вихідний масив тим самим об'єктом, видаливши лише елементи:

Array.prototype.removeContained = function(array) {
  var i, results;
  i = this.length;
  results = [];
  while (i--) {
    if (array.indexOf(this[i]) !== -1) {
      results.push(this.splice(i, 1));
    }
  }
  return results;
};

Або еквівалент CoffeeScript:

Array.prototype.removeContained = (array) ->
  i = @length
  @splice i, 1 while i-- when array.indexOf(@[i]) isnt -1

Тестування всередині інструментів хромованого розробника:

19: 33: 04,447 a = 1
19: 33: 06,354 b = 2
19: 33: 07,615 c = 3
19: 33: 09,981 arr = [a, b, c]
19: 33: 16,460 arr1 = arr

19: 33: 20,317 arr1 === arr
19: 33: 20,331 вірно

19: 33: 43,592 arr.removeCon sadrža ([a, c])
19: 33: 52,433 arr === arr1
19: 33: 52,438 вірно

Використання Angular Framework - це найкращий спосіб зберегти вказівник на вихідний об’єкт, коли ви оновлюєте колекції без великої кількості спостерігачів і перезавантажень.


Ця відповідь погана, оскільки втрачає кращі практики. Зокрема, ніколи не змінюючи об’єкти, якими ви не володієте. У цьому випадку ви змінюєте об’єкт Array, який є великим ні-ні.
Гібридна веб-

0

Я будую логіку, не використовуючи ніяких вбудованих методів, повідомте про будь-які оптимізації чи модифікації. Я перевірив у редакторі JS, він працює чудово.

var myArray = [
            {name: 'deepak', place: 'bangalore'},
            {name: 'alok', place: 'berhampur'},
            {name: 'chirag', place: 'bangalore'},
            {name: 'chandan', place: 'mumbai'},

        ];
        var toRemove = [

            {name: 'chirag', place: 'bangalore'},
            {name: 'deepak', place: 'bangalore'},
            /*{name: 'chandan', place: 'mumbai'},*/
            /*{name: 'alok', place: 'berhampur'},*/


        ];
        var tempArr = [];
        for( var i=0 ; i < myArray.length; i++){
            for( var j=0; j<toRemove.length; j++){
                var toRemoveObj = toRemove[j];
                if(myArray[i] && (myArray[i].name === toRemove[j].name)) {
                    break;
                }else if(myArray[i] && (myArray[i].name !== toRemove[j].name)){
                        var fnd = isExists(tempArr,myArray[i]);
                        if(!fnd){
                            var idx = getIdex(toRemove,myArray[i])
                            if (idx === -1){
                                tempArr.push(myArray[i]);
                            }

                        }

                    }

                }
        }
        function isExists(source,item){
            var isFound = false;
            for( var i=0 ; i < source.length; i++){
                var obj = source[i];
                if(item && obj && obj.name === item.name){
                    isFound = true;
                    break;
                }
            }
            return isFound;
        }
        function getIdex(toRemove,item){
            var idex = -1;
            for( var i=0 ; i < toRemove.length; i++){
                var rObj =toRemove[i];
                if(rObj && item && rObj.name === item.name){
                    idex=i;
                    break;
                }
            }
            return idex;
        }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.