1

Not sure how do to this, so any help is greatly appreciated

Say I have :

const array1 = [1, 1, 2, 3, 4];
const array2 = [1, 2];

Desired output

const result = [1, 3, 4];

I wish to compare array1 and array2 and for each entry in array2, remove the equivalent from array1. So if I have 3 of 1 in array1 and 1 of 1 in array2, the resulting array should have 2 of 1.

Working on a project that has both jquery and underscore.js if that makes anything easier.

0

7 Answers 7

4

var array1 = [1, 1, 2, 3, 4],
  array2 = [1, 2],
  result = array1.slice(0);

array2.forEach(function(element) {
  var index = result.indexOf(element)
  if (index >= 0) {
    result.splice(index, 1)
  }
})
console.log(result)

Sign up to request clarification or add additional context in comments.

3 Comments

Lovely algorithm
That was perfect, and plus, I understand whats happening!
Simple is the best :)
1

This will run reasonably well. I think its linear time instead of N*N

function diffOnlyOncePerElementInstance(a1, a2) {
  const max = Math.max(a1.length, a2.length);
  const map = {};

  for (let i = 0; i < max; i++) {
    const valueA = a1[i];
    const valueB = a2[i];
    if (i < a1.length) {
      if (!Number.isInteger(map[valueA])) {
        map[valueA] = 0;
      }
      map[valueA]++;
    }
    if (i < a2.length) {
      if (!Number.isInteger(map[valueB])) {
        map[valueB] = 0;
      }
      map[valueB]--
    }
  }

  return Object.keys(map)
    .map(key => new Array(Math.abs(map[key])).fill(key)) // regenerate remaining count
    .reduce((a,b) => a.concat(b), []); // flatten
}

Comments

1

IMO using array2 as object instead of array will be most performant way.

Here on first find of key we change the value in object so we do not filter that value again as desired in output.

const array1 = [1, 1, 2, 3, 4];
const array2 = Object.create(null,{
  1:{writable: true,value:false}, 
  2:{writable: true,value:false}
})


let op = array1.filter(e=> {
  if(array2[e] === false){
    array2[e] = true
    return false
  }
  return true
})

console.log(op)

On Side note:- With object.create we are creating a object without a prototype so it do not search for values in complete prototype chain.

2 Comments

Can improve that a bit by making array2 not have a prototype (so the JavaScript engine doesn't have to go looking for missing properties up the prototype chain), or using Map rather than an object (this answer builds a Map from the existing array2, which is a common approach if your starting point is two arrays).
@T.J.Crowder thanks for you inputs. i have updated answer.
0

You can try the following. Loop through array1 and check if the element of array2 exists on array1 and splice it out.

const array1 = [1, 1, 2, 3, 4];
const array2 = [1, 2];


for(i=0;i<=array1.length;i++){

  for(j=0;j<array2.length;j++){
    if(array2[j] == array1[i]){
      array1.splice(i,1);
    }
  }

}
console.log(array1);

Comments

0

If you are going to work with large arrays, this solution will perform very well. First convert array1 to a Map to make your lookups quick. Then go through array2 and subtract from your map to signify that the element should get removed. Then finally run through your map and add the keys that have values bigger than 0

const array1 = [1, 1, 2, 3, 4];
const array2 = [1, 2];

const obj = array1.reduce((acc, cv) => {
    if (acc.has(cv)) acc.set(cv, acc.get(cv) + 1);
    else acc.set(cv, 1);
    return acc;
}, new Map());

array2.forEach(i => {
    if (obj.has(i)) obj.set(i, obj.get(i) - 1);
});

const res = [];
obj.forEach((v, k) => { if (v) res.push(k); });
console.log(res)

Comments

0

Not sure the most efficiency way to do this in modern JS, and I'm pretty old-school, so here's an old-school solution:

 var array1 = [1, 1, 2, 3, 4];
    var array2 = [1, 2];
    
    // Note this method is destructive
    Array.prototype.removeFirstValueMatch = function(ar)
    {
    	var indexesToRemoveAr = [];
    	var indexesToRemoveOb = {};
    	for(var i=0, j; i<ar.length; i++)
    	{
    		for(j=0; j<this.length; j++)
    		{
    			if(this[j] == ar[i] && !indexesToRemoveOb.hasOwnProperty(j) )
    			{
    				indexesToRemoveOb[j] = indexesToRemoveAr.length;
    				indexesToRemoveAr.push(j);
    				break;
    			}
    		}
    	}
    	var descending = indexesToRemoveAr.sort().reverse();
    	for(i=0; i<descending.length; i++)
    	{
    		this.splice(descending[i],1);
    	}
    
    	return this;
    };

    // Destructive
    console.log(array1.removeFirstValueMatch(array2));//[1, 3, 4]
    console.log(array1.removeFirstValueMatch(array2));//[3, 4]

    // Non-Destructive
    var array1 = [1, 1, 2, 3, 4];
    console.log(array1.slice(0).removeFirstValueMatch(array2));//[1, 3, 4]
    console.log(array1.slice(0).removeFirstValueMatch(array2));//[1, 3, 4]

Comments

0

You could take a Map for counting the items for removing.

const
    array1 = [1, 1, 2, 3, 4],
    array2 = [1, 2],
    remove = array2.reduce((m, v) => m.set(v, (m.get(v) || 0) + 1), new Map),
    result = array1.filter(v => !remove.get(v) || !remove.set(v, remove.get() - 1));

console.log(...result);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.