Удалить все элементы, содержащиеся в другом массиве

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;
});

Есть ли чистый способ сделать это без зацикливания и объединения?

Нажмите
источник
1
Возможный дубликат Удалить конкретный элемент из массива?
mplungjan
4
возможный дубликат разницы в массиве JavaScript
Explosion Pills
1
Возможные дубликаты Можете ли вы удалить один массив из другого в javascript или jquery - вы не могли уделить много внимания предложениям, сделанным при написании вопроса
mplungjan
Независимо от того, что это всегда будет включать в себя цикл на каком-то уровне.
Голубое небо
Если вы действительно хотите, чтобы он был «эффективным», вы не будете использовать методы функционального типа, такие как .filter(). Вместо этого вы будете использовать forпетли. Вы можете избежать, .splice()если первоначальный заказ не нужно поддерживать. Или есть способы сделать .splice()более эффективными, если вы думаете, что будет много элементов для удаления.
голубое небо

Ответы:

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
ОП: Если вы используете Underscore.js, то это в .difference()основном и делает.
Билл Крисуэлл
Как раз то, что я искал. Спасибо. @BillCriswell, я проверю подчеркивание.
Нажмите
1
@AlecRust Преобразование всех элементов toRemove()в верхний регистр и изменение обратного вызова с elна el.toUpperCase().
Сирко
5
или короче:myArray = myArray.filter( el => !toRemove.includes( el ) );
538ROMEO
1
не этот порядок п ^ 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);
user2582833
источник
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);
    }
  }
}
MARCOL
источник
фильтр не «новый материал HTML5»
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) ))
мойтаба роохи
источник
Не могли бы вы добавить какое-нибудь объяснение по такому хорошо принятому вопросу, пожалуйста?
harmonica141
1
Я искал часы для решения проблемы и просто нашел ее, просто потрясающе. Большое спасибо!
Tarvo Mäesepp
5

Теперь в однострочном аромате:

console.log(['a', 'b', 'c', 'd', 'e', 'f', 'g'].filter(x => !~['b', 'c', 'g'].indexOf(x)))

Может не работать на старых браузерах.

Матас Вайткявичюс
источник
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

Craciun Ciprian
источник
Что если атрибут вложен в объект? Что-то вроде теста в вашем случае {name: 'deepak', place: 'bangalore', nested: {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)

Eggon
источник
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

Тестирование внутри Chrome Dev Tools:

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 обр1 === обр
19: 33: 20,331 верно

19: 33: 43.592 arr.removeContained ([a, c])
19: 33: 52.433 arr === arr1
19: 33: 52.438 true

Использование 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;
        }
Шраван Р
источник