This question already has answers here:
How to get the difference between two arrays in JavaScript?
(84 answers)
Closed 5 months ago.
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?
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 ) );
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).
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) ))
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]);
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));
Lodash has an utility function for this as well:
https://lodash.com/docs#difference
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)
I just implemented as:
Array.prototype.exclude = function(list){
return this.filter(function(el){return list.indexOf(el)<0;})
}
Use as:
myArray.exclude(toRemove);
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
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);
}
}
}
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.
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)
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])
);
}
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.
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;
}
//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' ]
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]);
};
Related
This is driving me crazy.
I have an array like so:
myArray = ['A', 'B', 'C', 'D', 'E', 'F', ];
and my code is making a NEW object that will look like this:
myNewObj = [
{
name: "A",
displayName: "A"
},
{
name: "B",
displayName: "B"
},
{
name: "C",
displayName: "C"
},
{
name: "C",
displayName: "C"
},
{
name: "D",
displayName: "D"
},
{
name: "E",
displayName: "E"
},
{
name: "F",
displayName: "F"
}
]
My code gets values in the myArray and I want to create myNewObj
This is how I'm doing it so far
I create the new object
let newObj = [{
"name": "",
"displayName": ""
}];
Then I use a simple FOR NEXT loop and it work up until a point and claims newObj[i].name IS UNDEFINED... how is that possible when I'm defining it and see it in the Google dev console?
for (let i = 0; i < myArray.length; i++) {
newObj[i].name = myArray[i];
newObj[i].displayName = myArray[i];
myNewObj.push(newObj);
}
However, I consistently get this error:
newObj[i].name is NOT DEFINED and it dies.
UPDATE: Everything's working and here's the ACTUAL real data
But as you can see, the prototype is there and when I save to sessionStorage, I get [Object,Object], x 12....
do I need to strip out name: and displayName: and put into yet another object?
Using Array#map:
const myArray = ['A', 'B', 'C', 'D', 'E', 'F'];
const myNewArray = myArray.map(e => ({ name: e, displayName: e }));
console.log(myNewArray);
Your solution (you need to initialize the array as [] and create a new object in every iteration to push it):
const myArray = ['A', 'B', 'C', 'D', 'E', 'F'];
const myNewArray = [];
for (let i = 0; i < myArray.length; i++) {
const obj = {};
obj.name = myArray[i];
obj.displayName = myArray[i];
myNewArray.push(obj);
}
console.log(myNewArray);
Issue with the code
You are doing the Array.Push incorrectly. Your let newObj should be an object, not an array. It should be initialized inside the loop. so that each execution takes a different memory location. You should define myNewObj as an empty array and should push your newObj into myNewObj array inside the loop.
Working Fiddle
const myArray = ['A', 'B', 'C', 'D', 'E', 'F'];
const myNewObj = [];
for (let i = 0; i < myArray.length; i++) {
let newObj = {
"name": "",
"displayName": ""
};
newObj.name = myArray[i];
newObj.displayName = myArray[i];
myNewObj.push(newObj);
};
console.log(myNewObj);
Better Approach
A better approach for this issue is to use Array.map. Array.map returns a new array from an existing array. You could run Array.map on myArray and construct myNewObj array as below.
Working Fiddle
const myArray = ['A', 'B', 'C', 'D', 'E', 'F'];
const myNewObj = myArray.map((item) => ({ name: item, dispayName: item}));
console.log(myNewObj)
var myArray = ['A', 'B', 'C', 'D', 'E', 'F', ];
var myNewObj = [];
for (let i = 0; i < myArray.length; i++) {
var newObj = {
name:'',
displayName:''
};
newObj['name'] = myArray[i];
newObj['displayName'] = myArray[i];
myNewObj.push(newObj);
}
console.log(myNewObj);
You're trying to define newObj as an array with one object for some reason. Just create a new object.
Create a new object within the scope of the loop for each iteration. You can then just push that object into the empty output array.
const letters = ['A', 'B', 'C', 'D', 'E', 'F'];
const arr = [];
for (let i = 0; i < letters.length; i++) {
const obj = {};
obj.name = letters[i];
obj.displayName = letters[i];
arr.push(obj);
}
console.log(arr);
Below is an array containing some elements:
const arr = ['a', 'b', 'c', 'a', 'b', 'c', 'd']
So how can I create a new array where same elements are grouped together into a new array like this:
const arr = [['a','a'], ['b','b'], ['c','c'], ['d']]
Thank you for your time.
This can be achieved with the most generic of group by operations.
const arr = ['a', 'b', 'c', 'a', 'b', 'c', 'd'];
const grouped = Object.values(arr.reduce((a, n) => ((a[n] ??= []).push(n), a), {}));
console.log(grouped);
This is one way to do it. More explicit, but easier to understand and translate to other languages as well. Time: O(n), Space: O(n), n is number of elements in array
function process(arr) {
const map = arr.reduce((acc, e) => {
if (!acc.has(e)) {
acc.set(e, 0);
}
acc.set(e, acc.get(e) + 1);
return acc;
}, new Map())
const res = [];
for (const[k, v] of map.entries()) {
const localRes = [];
for (let i = 1; i <= v; i++) {
localRes.push(k);
}
res.push(localRes);
}
return res;
}
const arr = ['a', 'b', 'c', 'a', 'b', 'c', 'd']
console.log(process(arr));
Result:
[ [ 'a', 'a' ], [ 'b', 'b' ], [ 'c', 'c' ], [ 'd' ] ]
The idea is to sort the array in ascending order, then iterate over it and take if the last char and the current char are the same and put them in an inner-array else create new inner-array of that char, do this process of accumulating till for loop iterate overall characters.
["a","a","b","b","c","c","d"]
//sort and do algorithm
["a","a","b","b","c","c","d"]
//^---^ ^---^
[["a","a"],["b","b"],["c","c"],["d"]]
Implementation:
const arr = ["a", "b", "c", "a", "b", "c", "d"];
const chars = arr.sort((a, b) => a.localeCompare(b));
console.log(chars);
let res = [[]],
lastChar = chars[0];
for (char of chars) {
if (char == lastChar) {
res[res.length - 1].push(char);
} else {
res.push([char]);
lastChar = char;
}
}
Result:
console.log(res); //[["a","a"],["b","b"],["c","c"],["d"]]
I have a array has a value of ['a', 'b', 'c', 'd', 'e'] now I want it to become a object that have it's value so I do some array mapping
const arrs = ['a', 'b', 'c', 'd', 'e']
let arrObj = arrs.map(arr => {
return {value: arr}
})
Now the value of arrObj is
[{value: 'a'}, {value: 'b'}, {value: 'c'}, {value: 'd'}, {value: 'e'}]
But what I want to do is to insert a object in the middle of each object that is inside the array that has a value of {operator: '+'} so the value of arrObj will be
[{value: 'a'}, {operator: '+'}, {value: 'b'}, {operator: '+'}, {value: 'c'}, {operator: '+'}, {value: 'd'}, {operator: '+'}, {value: 'e'}]
now, using javascript, how can I achive that function given that I'm setting a value of arrObj in array.map() ?
You could create a new array with a double length minus one and add the required values, depending on the index.
var values = ['a', 'b', 'c', 'd', 'e'],
result = Array.from(
{ length: values.length * 2 - 1 },
(_, i) => i % 2
? { operator: '+' }
: { value: values[i >> 1] }
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
One option is to map each element (but the last) to an array with that element and another with the +, then flatten:
const arrs = ['a', 'b', 'c', 'd', 'e'];
const transformed = arrs
.map((char, i) => (
i === arrs.length - 1
? [{ value: char }]
: [{ value: char }, { value: '+' }]
))
.flat();
console.log(transformed);
If + won't appear in the input array, then you can join by + initially, then split:
const arrs = ['a', 'b', 'c', 'd', 'e'];
const output = arrs
.join('+')
.split('')
.map(value => ({ value }));
console.log(output);
Use flatMap and return a pair from the callback. Finally, remove the extra element.
const arrs = ['a', 'b', 'c', 'd', 'e']
let arrObj = arrs.flatMap(x => [
{operator: '+'}, {value: x}
]).slice(1)
console.log(arrObj)
If your platform doesn't have flatMap yet, it's trivial to polyfill:
Array.prototype.flatMap = function(fn) {
return this.concat.apply([], this.map(fn))
}
Generic function:
let interleave = (ary, val) => ary.flatMap(x => [val, x]).slice(1);
//
let arrObj = interleave(
arrs.map(x => ( {value: x})),
{operator: '+'}
)
What about using reduce?
let arrObj = arrs.reduce((acc, curr, index) => {
if (index === arrs.length - 1) acc = [...acc, {value: curr}];
else acc = [...acc, {value: curr}, {operator: '+'}];
return acc;
}, [])
Concat your required object and deep flatten it like below:
var arrs = ['a', 'b', 'c', 'd','e'];
const arrObj = arrs.map((arr,i) => {
let item = [{value: arr}];
if(i < arrs.length-1)
item.push({operator: '+'});
return item;
});
console.log(flattenDeep(arrObj));
function flattenDeep(arr1) {
return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
}
For more details take a look here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
Example object array:
[{
id: 'a',
beforeId: null
}, {
id: 'b',
beforeId: 'c'
}, {
id: 'c',
beforeId: 'a'
}, {
id: 'd',
beforeId: 'b'
}]
Output order: d-b-c-a; each element sorted relative to each other element based on its beforeId property.
I could make a temporary array and sort the above array. Is sorting possible with array.sort?
You could build an object with the relations and generate the result by using the object with beforeId: null and unshift all objects for the result array.
The next object is the one with the actual val as key.
Complexity: O(2n).
function chain(array) {
var o = {}, pointer = null, result = [];
array.forEach(a => o[a.beforeId] = a);
while (o[pointer]) {
result.unshift(o[pointer]);
pointer = o[pointer].val;
}
return result;
}
var data = [{ val: 'a', beforeId: null }, { val: 'b', beforeId: 'c' }, { val: 'c', beforeId: 'a' }, { val: 'd', beforeId: 'b' }];
console.log(chain(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is a terribly inefficient and naïve algorithm, but it works:
const array = [
{id: 'a', beforeId: null},
{id: 'b', beforeId: 'c'},
{id: 'c', beforeId: 'a'},
{id: 'd', beforeId: 'b'}
];
// find the last element
const result = [array.find(i => i.beforeId === null)];
while (result.length < array.length) {
// find the element before the first element and prepend it
result.unshift(array.find(i => i.beforeId == result[0].id));
}
console.log(result);
Is sorting possible with array.sort?
sure, with a helper function:
graph = [
{id: 'a', beforeId: null},
{id: 'b', beforeId: 'c'},
{id: 'c', beforeId: 'a'},
{id: 'd', beforeId: 'b'}
];
let isBefore = (x, y) => {
for (let {id, beforeId} of graph) {
if (id === x)
return (beforeId === y) || isBefore(beforeId, y);
}
return false;
};
graph.sort((x, y) => x === y ? 0 : (isBefore(x.id, y.id) ? -1 : +1))
console.log(graph);
isBefore returns true if x is before y immediately or transitively.
For generic, non-linear topological sorting see https://en.wikipedia.org/wiki/Topological_sorting#Algorithms
UPD: As seen here, this turned out to be horribly inefficient, because sort involves many unnecessary comparisons. Here's the fastest (so far) version:
function sort(array) {
let o = {}, res = [], len = array.length;
for (let i = 0; i < len; i++)
o[array[i].beforeId] = array[i];
for (let i = len - 1, p = null; i >= 0; i--) {
res[i] = o[p];
p = o[p].id;
}
return res;
}
which is the #Nina's idea, optimized for speed.
You may try this approach :
// order(null, vals, []) = ["d", "b", "c", "a"]
function order(beforeId, vals, result){
var id = beforeId || null;
var before = vals.filter(function(val){
return val.beforeId === id
});
if (before.length === 0) return result;
return order(before[0].val,
vals,
[before[0].val].concat(result));
}
How can I create an object from two arrays without using loops in javascript.
example:
array1 = [1,2,3,4,5];
array2 = [A,B,C,D,E];
I want from below object
obj = {
'1': 'A',
'2': 'B',
'3': 'C',
'4': 'D',
'5': 'E',
}
Thanks in advance
var obj = {}
array1 = [1, 2, 3, 4, 5];
array2 = ['A', 'B', 'C', 'D', 'E'];
array1.forEach(function(value, index) {
obj[value] = array2[index];
});
console.log(obj);
Try to use $.each() to iterate over one of that array and construct the object as per your requirement,
var array1 = [1,2,3,4,5],array2 = ['A','B','C','D','E'];
var obj = {};
$.each(array2,function(i,val){
obj[array1[i]] = val;
});
DEMO
An ES6, array reduce solution.
const array1 = [1, 2, 3, 4, 5];
const array2 = ['A', 'B', 'C', 'D', 'E'];
const resultMap = array1.reduce(
(accumulator, value, index) => Object.assign(accumulator, {
[value]: array2[index],
}), {}
);
console.log(resultMap);
just for fun created something like this without using any iteration methods.
const array1 = [1,2,3,4,5];
const array2 = ['A','B','C','D','E'];
let combineKeyValueProxy = new Proxy({}, {
set: function(target, prop, value, receiver) {
target[array1[prop]] = value;
return true
}
});
const output = Object.assign(combineKeyValueProxy, array2);
console.log(output) // Proxy {1: "A", 2: "B", 3: "C", 4: "D", 5: "E"}