Unexpected result when filtering one object array against two other object arrays

匆匆过客 提交于 2020-03-23 08:50:09

问题


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?


回答1:


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));



回答2:


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);
}



回答3:


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);


来源:https://stackoverflow.com/questions/56933353/unexpected-result-when-filtering-one-object-array-against-two-other-object-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!