1

I'm trying to filter out objects from array "a" that match with objects in array "b" and "c". here is a link to jsfiddle to test the code.

Here is what I currently have:

const a = [{
  "name": "sondre",
  "uq_id": "abc1"
}, {
  "name": "sofie",
  "uq_id": "abc2"
}, {
  "name": "casper",
  "uq_id": "abc3"
}, {
  "name": "odin",
  "uq_id": "abc4"
}];

const b = [{
  "name": "sondre",
  "uq_id": "abc1"
}, {
  "name": "odin",
  "uq_id": "abc4"
}];

const c = [{
  "name": "casper",
  "uq_id": "abc3"
}];

function sort(a, b, c) {
  result = [];
  console.log(result);
  if (b !== null) {
    result = a.filter(function(item) {
      return !b.includes(item.uq_id);
    })
  }
  if (c !== null) {
    result = result.filter(function(item) {
      return !c.includes(item.uq_id);
    })
  }
  console.log(result);
}

sort(a, b, c);

I expect the following output:

[{name="sofie", uq_id="abc2"}]

But for some reason it outputs:

[{name="sondre", uq_id="abc1"},
{name="sofie", uq_id="abc2"},
{name="casper", uq_id="abc3"},
{name="odin", uq_id="abc4"}]

Does anyone know how I can get this to work as I intended it?

Thiyagu
  • 17,362
  • 5
  • 42
  • 79
  • 1
    *"heres a link to jsfiddle to test the code"* Please do your runnable example **here, on-site** using Stack Snippets (the `[<>]` toolbar button; [here's how to do one](https://meta.stackoverflow.com/questions/358992/)). – T.J. Crowder Jul 08 '19 at 10:53
  • Why is the function called `sort`? It doesn't do any sorting. – T.J. Crowder Jul 08 '19 at 10:53
  • ^ indeed, it does more "filtering". Or rather intersection between three arrays. – VLAZ Jul 08 '19 at 10:54

3 Answers3

2

If the goal is to filter out entries from a whose name matches an entry on either b or c, you can't use includes unless the entries in a, b, and c refer to the same objects (not just equivalent ones).

Assuming they don't, you can use some to find out whether an array contains a match for a name. You'll want to use && to see that there's no match in either b or c:

const filtered = a.filter(entry => {
    return !b.some(({name}) => entry.name === name) &&
           !c.some(({name}) => entry.name === name);
});

Live Copy:

const a = [{
  "name": "sondre",
  "uq_id": "abc1"
}, {
  "name": "sofie",
  "uq_id": "abc2"
}, {
  "name": "casper",
  "uq_id": "abc3"
}, {
  "name": "odin",
  "uq_id": "abc4"
}];

const b = [{
  "name": "sondre",
  "uq_id": "abc1"
}, {
  "name": "odin",
  "uq_id": "abc4"
}];

const c = [{
  "name": "casper",
  "uq_id": "abc3"
}];

function filter(a, b, c) {
    const filtered = a.filter(entry => {
        return !b.some(({name}) => entry.name === name) &&
               !c.some(({name}) => entry.name === name);
    });
    return filtered;
}

console.log(filter(a, b, c));

That can also be expressed with every, whichever you prefer:

const filtered = a.filter(entry => {
    return b.every(({name}) => entry.name !== name) &&
           c.every(({name}) => entry.name !== name);
});

If b and c are really large (hundreds of thousands of entries, perhaps millions) that could be inefficient enough to justify creating Sets of names first:

const names = new Set([
  ...b.map(({name}) => name),
  ...c.map(({name}) => name)
]);
const filtered = a.filter(entry => {
    return !names.has(entry.name);
});

Or you might just do that for preference or clarity.

Live Copy:

const a = [{
  "name": "sondre",
  "uq_id": "abc1"
}, {
  "name": "sofie",
  "uq_id": "abc2"
}, {
  "name": "casper",
  "uq_id": "abc3"
}, {
  "name": "odin",
  "uq_id": "abc4"
}];

const b = [{
  "name": "sondre",
  "uq_id": "abc1"
}, {
  "name": "odin",
  "uq_id": "abc4"
}];

const c = [{
  "name": "casper",
  "uq_id": "abc3"
}];

function filter(a, b, c) {
    const names = new Set([
      ...b.map(({name}) => name),
      ...c.map(({name}) => name)
    ]);
    const filtered = a.filter(entry => {
        return !names.has(entry.name);
    });
    return filtered;
}

console.log(filter(a, b, c));
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Apologies, I had a silly logic error (and typo) that I didn't catch until doing the runnable examples. :-/ – T.J. Crowder Jul 08 '19 at 11:04
  • How would this handle B or C being empty, undefined or null? – Sondre Ljovshin Jul 08 '19 at 11:20
  • @SondreLjovshin - Empty isn't a problem. If they might be `undefined` or `null`, you'll want to handle that. Probably the easiest way is `if (!b) { b = []; } if (!c) { c = []; }` before you start the filtering. – T.J. Crowder Jul 08 '19 at 11:31
1

b and c are arrays of objects it only includes objects. You need to use some() on then to compare uq_id

const a = [{
    "name": "sondre",
    "uq_id": "abc1"
}, {
    "name": "sofie",
    "uq_id": "abc2"
}, {
    "name": "casper",
    "uq_id": "abc3"
}, {
    "name": "odin",
    "uq_id": "abc4"
}];

const b = [{
    "name": "sondre",
    "uq_id": "abc1"
}, {
    "name": "odin",
    "uq_id": "abc4"
}];

const c = [{
    "name": "casper",
    "uq_id": "abc3"
}];

sort(a, b, c);



function sort(a, b, c) {
    let result = [];
    if (b !== null) {
        result = a.filter(function(item) {
            return !b.some(x => x.uq_id === item.uq_id);
        })
    }
    if (c !== null) {
        result = result.filter(function(item) {
            return !c.some(x => x.uq_id === item.uq_id);
        })
    }
    console.log(result);
}
Maheer Ali
  • 35,834
  • 5
  • 42
  • 73
0

Single filter is enough :

const a = [ { "name": "sondre", "uq_id": "abc1" }, 
            { "name": "sofie" , "uq_id": "abc2" }, 
            { "name": "casper", "uq_id": "abc3" }, 
            { "name": "odin"  , "uq_id": "abc4" } ];

const b = [ { "name": "sondre", "uq_id": "abc1" }, 
            { "name": "odin"  , "uq_id": "abc4" } ];

const c = [ { "name": "casper", "uq_id": "abc3" } ];


const result = a.filter(x => !b.some(y => x.uq_id === y.uq_id) 
                          && !c.some(y => x.uq_id === y.uq_id));

console.log(result);
Slai
  • 22,144
  • 5
  • 45
  • 53