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?
This question is related to
javascript
arrays
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"]
_x000D_
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).
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
Now in one-liner flavor:
console.log(['a', 'b', 'c', 'd', 'e', 'f', 'g'].filter(x => !~['b', 'c', 'g'].indexOf(x)))
_x000D_
Might not work on old browsers.
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) ))
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])
);
}
How about the simplest possible:
var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];_x000D_
var toRemove = ['b', 'c', 'g'];_x000D_
_x000D_
var myArray = myArray.filter((item) => !toRemove.includes(item));_x000D_
console.log(myArray)
_x000D_
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 = arr19:33:20.317 arr1 === arr
19:33:20.331 true19: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.
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]);
I just implemented as:
Array.prototype.exclude = function(list){
return this.filter(function(el){return list.indexOf(el)<0;})
}
Use as:
myArray.exclude(toRemove);
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 = [_x000D_
{name: 'deepak', place: 'bangalore'}, _x000D_
{name: 'chirag', place: 'bangalore'}, _x000D_
{name: 'alok', place: 'berhampur'}, _x000D_
{name: 'chandan', place: 'mumbai'}_x000D_
];_x000D_
var toRemove = [_x000D_
{name: 'deepak', place: 'bangalore'},_x000D_
{name: 'alok', place: 'berhampur'}_x000D_
];_x000D_
_x000D_
for( var i=myArray.length - 1; i>=0; i--){_x000D_
for( var j=0; j<toRemove.length; j++){_x000D_
if(myArray[i] && (myArray[i].name === toRemove[j].name)){_x000D_
myArray.splice(i, 1);_x000D_
}_x000D_
}_x000D_
}_x000D_
_x000D_
alert(JSON.stringify(myArray));
_x000D_
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);
}
}
}
Lodash has an utility function for this as well: https://lodash.com/docs#difference
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;
}
Source: Stackoverflow.com