Remove all elements contained in another array

JavascriptArrays

Javascript Problem Overview


I am looking for an efficient way to remove all elements from a javascript array if they are present in another array.

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

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

I want to operate on myArray to leave it in this state: ['a', 'd', 'e', 'f']

With jQuery, I'm using grep() and inArray(), which works well:

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

Is there a pure javascript way to do this without looping and splicing?

Javascript Solutions


Solution 1 - Javascript

Use the Array.filter() method:

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


Small improvement, as browser support for Array.includes() has increased:

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


Next adaptation using arrow functions:

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

Solution 2 - Javascript

ECMAScript 6 sets can permit faster computing of the elements of one array that aren't in the other:

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

const difference = myArray.filter( x => !toRemove.has(x) );

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

Since the lookup complexity for the V8 engine browsers use these days is O(1), the time complexity of the whole algorithm is O(n).

Solution 3 - Javascript

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

Solution 4 - Javascript

The filter method should do the trick:

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

If your toRemove array is large, this sort of lookup pattern can be inefficient. It would be more performant to create a map so that lookups are O(1) rather than 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]);

Solution 5 - Javascript

If you are using an array of objects. Then the below code should do the magic, where an object property will be the criteria to remove duplicate items.

In the below example, duplicates have been removed comparing name of each item.

Try this example. 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));

Solution 6 - Javascript

Lodash has an utility function for this as well: https://lodash.com/docs#difference

Solution 7 - Javascript

How about the simplest possible:

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)

Solution 8 - Javascript

I just implemented as:

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

Use as:

myArray.exclude(toRemove);

Solution 9 - Javascript

If you cannot use new ES5 stuff such filter I think you're stuck with two loops:

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

Solution 10 - Javascript

Now in one-liner flavor:

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

Might not work on old browsers.

Solution 11 - Javascript

You can use _.differenceBy from 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');

Example code here: CodePen

Solution 12 - Javascript

This is pretty late but adding this to explain what @mojtaba roohi has answered. The first block of code will not work as each array is having a different object, i.e. df[0] != nfl[2]. Both objects look similar but are altogether different, which is not the case when we use primitive types like numbers.

let df = [ {'name': 'C' },{'name': 'D' }] 
let nfl = [ {'name': 'A' },{'name': 'B' },{'name': 'C' },{'name': 'D' }] 
let res = nfl.filter(x => df.indexOf(x)<0)
console.log(res)

Here is the working code:

let df = [{'name': 'C' },{'name': 'D' }]
let nfl = [ {'name': 'A' },{'name': 'B' },{'name': 'C' },{'name': 'D' }];
let res = nfl.filter((o1) => !df.some((o2) => o1.name === o2.name));
console.log(res)

Solution 13 - Javascript

If you're using Typescript and want to match on a single property value, this should work based on Craciun Ciprian's answer above.

You could also make this more generic by allowing non-object matching and / or multi-property value matching.

/**
 *
 * @param arr1 The initial array
 * @param arr2 The array to remove
 * @param propertyName the key of the object to match on
 */
function differenceByPropVal<T>(arr1: T[], arr2: T[], propertyName: string): T[] {
  return arr1.filter(
    (a: T): boolean =>
      !arr2.find((b: T): boolean => b[propertyName] === a[propertyName])
  );
}

Solution 14 - Javascript

Proper way to remove all elements contained in another array is to make source array same object by remove only elements:

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

Or CoffeeScript equivalent:

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

Testing inside 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 arr1 === arr
> 19:33:20.331 true
> > 19:33:43.592 arr.removeContained([a,c])
> 19:33:52.433 arr === arr1
> 19:33:52.438 true

Using Angular framework is the best way to keep pointer to source object when you update collections without large amount of watchers and reloads.

Solution 15 - Javascript

I build the logic without using any built-in methods, please let me know any optimization or modifications. I tested in JS editor it is working fine.

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

Solution 16 - Javascript

//Using the new ES6 Syntax

    console.log(["a", "b", "c", "d", "e", "f", "g"].filter(el => !["b", "c", "g"].includes(el)));

    // OR

    // Main array
    let myArray = ["a", "b", "c", "d", "e", "f", "g"];

    // Array to remove
    const toRemove = ["b", "c", "g"];

    const diff = () => (myArray = myArray.filter((el) => !toRemove.includes(el)));
  console.log(diff()); // [ 'a', 'd', 'e', 'f' ]

    // OR

    const diff2 = () => {
      return myArray = myArray.filter((el) => !toRemove.includes(el));
    };
    console.log(diff2()); // [ 'a', 'd', 'e', 'f' ]

Solution 17 - Javascript

A High performance and immutable solution

Javascript

const excludeFromArr = (arr, exclude) => {
  const excludeMap = exclude.reduce((all, item) => ({ ...all, [item]: true }), {});
  return arr.filter((item) => !excludeMap?.[item]);
};

Typescript:

const excludeFromArr = (arr: string[], exclude: string[]): string[] => {
  const excludeMap = exclude.reduce<Record<string, boolean>>((all, item) => ({ ...all, [item]: true }), {});
  return arr.filter((item) => !excludeMap?.[item]);
};

Solution 18 - Javascript

const first = [
  { key: "Value #1", key1: "Value #11" },
  { key: "Value #2", key1: "Value #12" },
  { key: "Value #3", key1: "Value #13" },
];

const second = [{ key: "Value #1", key1: "Value #11" }];

const toRemove = second.map((x) => Object.values(x).join("."));

const remainingData = first.filter( (x) => !toRemove.includes(Object.values(x).join(".")) );

console.log(JSON.stringify(remainingData, null, 2));

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTapView Question on Stackoverflow
Solution 1 - JavascriptSirkoView Answer on Stackoverflow
Solution 2 - JavascriptBenny NeugebauerView Answer on Stackoverflow
Solution 3 - Javascriptmojtaba roohiView Answer on Stackoverflow
Solution 4 - JavascriptAshwin BalamohanView Answer on Stackoverflow
Solution 5 - JavascriptDeepak AcharyaView Answer on Stackoverflow
Solution 6 - JavascriptCrenshinibonView Answer on Stackoverflow
Solution 7 - JavascriptEggonView Answer on Stackoverflow
Solution 8 - JavascriptRuneView Answer on Stackoverflow
Solution 9 - JavascriptMarcoLView Answer on Stackoverflow
Solution 10 - JavascriptMatas VaitkeviciusView Answer on Stackoverflow
Solution 11 - JavascriptCraciun CiprianView Answer on Stackoverflow
Solution 12 - JavascriptGopal MishraView Answer on Stackoverflow
Solution 13 - Javascriptonx2View Answer on Stackoverflow
Solution 14 - JavascriptМихаил ЮдинView Answer on Stackoverflow
Solution 15 - JavascriptShravan RView Answer on Stackoverflow
Solution 16 - JavascriptbrytebeeView Answer on Stackoverflow
Solution 17 - JavascriptMasih JahangiriView Answer on Stackoverflow
Solution 18 - JavascriptMuhammad ShafiqView Answer on Stackoverflow