0

Assuming I have the following array of objects

[{a:1,b:2,c:4}, {a:3,b:4,c:7}, {a:7,b:10,c:10}, {a:1, b:2,c:4}]

How do I select the unique items in this array. I need to compare both fields (a, b only, excluding c) and the examples I have seen show a map which takes a single field only.

tmp dev
  • 8,043
  • 16
  • 53
  • 108

3 Answers3

1

You can use a nested filter, and check that the number of matching elements with the same a and b is 1:

const input = [{a:1,b:2,c:4}, {a:3,b:4,c:7}, {a:7,b:10,c:10}, {a:1, b:2,c:4}];
const output = input.filter(
  ({ a, b }) => input.filter(obj => obj.a === a && obj.b === b).length === 1
);
console.log(output);

For O(N) complexity instead, reduce into an object indexed by num_num, or something like that, and then select only the values which have one item in them:

const input = [{a:1,b:2,c:4}, {a:3,b:4,c:7}, {a:7,b:10,c:10}, {a:1, b:2,c:4}];
const inputByAB = input.reduce((accum, obj) => {
  const key = `${obj.a}_${obj.b}`;
  if (!accum[key]) {
    accum[key] = [];
  }
  accum[key].push(obj);
  return accum;
}, {});
const output = Object.values(inputByAB)
  .filter(arr => arr.length === 1)
  .flat();
console.log(output);

If you can't use .flat, spread into concat instead:

const input = [{a:1,b:2,c:4}, {a:3,b:4,c:7}, {a:7,b:10,c:10}, {a:1, b:2,c:4}];
const inputByAB = input.reduce((accum, obj) => {
  const key = `${obj.a}_${obj.b}`;
  if (!accum[key]) {
    accum[key] = [];
  }
  accum[key].push(obj);
  return accum;
}, {});
const output = [].concat(...
  Object.values(inputByAB)
    .filter(arr => arr.length === 1)
);
console.log(output);
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
1

For dynamic keys create a function which takes array and arrays strings representing keys as input.

Create another helper method which compare the given keys of two objects.

Use filter() on the array of objects and then use every() element before the current index is not equal to the current object regarding given keys.

let arr = [{a:1,b:2,c:4}, {a:3,b:4,c:7}, {a:7,b:10,c:10}, {a:1, b:2,c:4}];
function comp(obj1,obj2,keys){
  return keys.every(k => obj1[k] === obj2[k]);
}
function getUnique(arr,keys){
  return arr.filter((x,i) => arr.slice(0,i).every(a => !comp(a,x,keys)))
}

console.log(getUnique(arr,['a','b']))
Maheer Ali
  • 35,834
  • 5
  • 42
  • 73
0

You could reduce the array with a Map as an accumulator. Use a combination of a and b properties as key so that duplicates are removed. Then use Map#values to get the output

const input = [{a:1,b:2,c:4}, {a:3,b:4,c:7}, {a:7,b:10,c:10}, {a:1, b:2,c:4}],
      mapped = input.reduce((map, o) => map.set(o.a + "_" + o.b, o), new Map),
      output = Array.from(mapped.values());

console.log(output)
adiga
  • 34,372
  • 9
  • 61
  • 83
  • Thanks for all your replies, I think I forgot to mention, I also need the a single item of the duplicated item, not to completely ignore them. The sample code give above removes the duplicates completely, I need one instance of it. – tmp dev Jun 03 '19 at 22:31
  • @tmpdev It doesn't remove the duplicates completely. It keeps one item for every unique `a` and `b`. The output has 3 items. Only one item with `a:1,b:2` is in the output – adiga Jun 04 '19 at 06:03
  • @tmpdev if this answers you question, please upvote and mark it as accepted. – adiga Jun 06 '19 at 17:44