1

Lets say I have two arrays as follows:

const A = ['Mo', 'Tu', 'We', 'Thu', 'Fr']
const B = ['Mo', 'Mo', 'Mo', 'Tu', 'Thu', 'Fr', 'Sa']

I want to subtract array A from array B. With the result looking like this:

const result = ['Mo', 'Mo', 'Sa']

How can this be achieved? It seems so simple but I cannot get it working.

Essentially this should remove everything from B once that is in A.

Tom el Safadi
  • 6,164
  • 5
  • 49
  • 102
  • Related but not quite the same thing: [What is the fastest or most elegant way to compute a set difference using Javascript arrays?](https://stackoverflow.com/questions/1723168/what-is-the-fastest-or-most-elegant-way-to-compute-a-set-difference-using-javasc) – jarmod Nov 16 '22 at 23:02
  • As @jarmod mentioned, the answer is in that post, check this answer: https://stackoverflow.com/a/36504668/7560262 – Mauricio Cárdenas Nov 16 '22 at 23:04
  • Doing a set difference will not contain duplicates in the result as a set can't have duplicates by nature – Tom el Safadi Nov 16 '22 at 23:05

4 Answers4

3

const A = ['Mo', 'Tu', 'We', 'Thu', 'Fr']
const B = ['Mo', 'Mo', 'Mo', 'Tu', 'Thu', 'Fr', 'Sa']

console.log(A.reduce((b, a)=>
  (b.includes(a) && b.splice(b.indexOf(a),1), b), [...B]))

Without the code golf:

const A = ['Mo', 'Tu', 'We', 'Thu', 'Fr']
const B = ['Mo', 'Mo', 'Mo', 'Tu', 'Thu', 'Fr', 'Sa']

console.log(A.reduce((b, a)=> {
  if(b.includes(a)) b.splice(b.indexOf(a), 1); return b; }, [...B]))
Andrew Parks
  • 6,358
  • 2
  • 12
  • 27
  • Might have to rephrase my title so it is easier to understand the type of problem – Tom el Safadi Nov 16 '22 at 23:22
  • 1
    @TomelSafadi my first attempt modified A in place. I just updated the answer to return the desired result while preserving the state of A and B – Andrew Parks Nov 16 '22 at 23:24
  • Do you know why I am getting an eslint error for variable `b` by any change? `Unexpected use of comma operator` – Tom el Safadi Nov 16 '22 at 23:56
  • @TomelSafadi Because ESLint doesn’t like the comma operator with the `no-sequences` rule enabled. See the [documentation](//eslint.org/docs/latest/rules/no-sequences). You can use `A.reduce((b, a) => { if(b.includes(a)){ b.splice(b.indexOf(a), 1); } return b; }, B.slice())` instead, so it doesn’t use this codegolf-style syntax. – Sebastian Simon Nov 17 '22 at 00:00
  • 1
    @TomelSafadi it's normally a useful warning, which can save you if you misplaced the comma and didn't mean to create a comma expression. However, we did intend to use a comma expression. If it bugs you, you can remove the warning by changing it to this: `console.log(A.reduce((b, a)=>b.includes(a) && b.splice(b.indexOf(a),1) && b || b, [...B]))` – Andrew Parks Nov 17 '22 at 00:00
2

A funny little filter() over a map of A.

const A = ['Mo', 'Tu', 'We', 'Thu', 'Fr']
const B = ['Mo', 'Mo', 'Mo', 'Tu', 'Thu', 'Fr', 'Sa']

const aMap = A.reduce((a, c) => (a[c] = (a[c] ?? 0) + 1, a), {})
const result = B.filter(n => !(aMap[n]-- > 0))

console.log(result)
pilchard
  • 12,414
  • 5
  • 11
  • 23
0

const A = ['Mo', 'Tu', 'We', 'Thu', 'Fr']
const B = ['Mo', 'Mo', 'Mo', 'Tu', 'Thu', 'Fr', 'Sa']

const C = B.map(el => {
    const elIndexInA = A.findIndex(e => e === el)
    if (elIndexInA === -1) {
        return el
    }
    A.splice(elIndexInA, 1)
}).filter(el => el)

console.log(C)
Mohamed Oraby
  • 916
  • 3
  • 8
  • 14
  • 1
    This mutates `A`. Introduce `const res = A.slice();` and replace each `A` by `res`. – Sebastian Simon Nov 16 '22 at 23:34
  • This also doesn’t work for `A = [ undefined ]; B = [ undefined, undefined ];`. Should be `[ undefined ]`, but yours produces `[]`. Another failing test case: `A = [ 1 ]; B = [ undefined, undefined ]` (should be `[ undefined, undefined ]`). – Sebastian Simon Nov 16 '22 at 23:57
  • @SebastianSimon well, my answer is related to code snippet in the question, you can configure it for your needs, I'm pretty sure in most cases you won't return `undefined` as a value – Mohamed Oraby Nov 17 '22 at 00:36
-1

Try this:

const A = ['Mo', 'Tu', 'We', 'Thu', 'Fr']
const B = ['Mo', 'Mo', 'Mo', 'Tu', 'Thu', 'Fr', 'Sa']
let res = B;
A.forEach(val => {
  for(let i = 0; i < res.length; i++) {
    if(res[i] === val) {
      res.splice(res.indexOf(val), 1);
      break;
    }
  }
});