1

I have a very simple array:

var arr = [{id: 1, score: 10}, {id: 1, score: 10}, {id: 3, score: 20}, {id: 4, score: 5}];

I want to remove those object which has only single occurrence e.g:

{id: 3, score: 20}
{id: 4, score: 5}

So the final output should be:

[{id: 1, score: 10}, {id: 1, score: 10}]

What I have tried so far is:

const result = [];

for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
        if (arr[i].id === arr[j].id && arr[i].score === arr[j].score) {
            result.push({ id: arr[i].id, score: arr[i].score })             
        }
    }
}

But this is removing my duplicates as well, e,g it gives me this result:

[{id: 1, score: 10}]

But i need this result:

[{id: 1, score: 10}, {id: 1, score: 10}]
DarkBee
  • 16,592
  • 6
  • 46
  • 58
StormTrooper
  • 1,731
  • 4
  • 23
  • 37

3 Answers3

6

Array.filter will help

Filter the array with combination of id and score, check if the length of this comination is greater than 1, which means there are more than one occurance of this combination.

const arr = [{ id: 1, score: 10 }, { id: 1, score: 10 }, { id: 3, score: 20 }, { id: 4, score: 5 }, { id: 77, score: 25, type: 'notthisone' }];
const newArr = arr.filter(item => arr.filter(x => x.id === item.id && x.score === item.score).length > 1 || item.type === 'notthisone');
console.log(newArr);

Your fixed solution.

You have to push the value from arr for both index i and j. But the index node for i must be pushed only once.

const arr = [{id: 1, score: 10}, {id: 1, score: 10}, {id: 3, score: 20}, {id: 4, score: 5}, {id: 77, score: 25, type: 'notthisone'}];
const result = [];

for (let i = 0; i < arr.length; i++) {
  let isFound = false;
  for (let j = i + 1; j < arr.length; j++) {
    if (arr[i].id === arr[j].id && arr[i].score === arr[j].score) {
      isFound = true;
      result.push(arr[j]);
    }
  }
  if(isFound ||  arr[i].type === 'notthisone') {
      result.push(arr[i]);
  }
}
console.log(result);

Edit: If you want to have one specific object having particular value for key type, then you can include that in the condition. I have updated the answer with that.

Nitheesh
  • 19,238
  • 3
  • 22
  • 49
  • This works fine. what if I am willing to remove specific non duplicate records? E,g those unique records whose type == 'notthisone' – StormTrooper Nov 15 '21 at 11:12
  • considering following array: const arr = [{id: 1, score: 10}, {id: 1, score: 10}, {id: 3, score: 20}, {id: 4, score: 5}, {id: 77, score: 25, type: 'notthisone'}]; Now here I am expecting following response: [{id: 1, score: 10}, {id: 1, score: 10}, {id: 77, score: 25, type: 'notthisone'}] – StormTrooper Nov 15 '21 at 11:15
  • 1
    @StormTrooper If you want to have one specific object having particular value for key `type`, then you can include that in the condition. I have updated the answer with that. – Nitheesh Nov 15 '21 at 12:51
1

You could take a single loop and an object for keeping track of the first of reference for the same item.

const
    array =  [{ id: 1, score: 10, x: 1 }, { id: 1, score: 10, x: 2, }, { id: 3, score: 20 }, { id: 4, score: 5 }, { id: 77, score: 25, type: 'notthisone' }],
    getKey = ({ id, score }) => JSON.stringify({ id, score }),
    result = array
        .map((q => o => {
            if (o.type === 'notthisone') return o;

            const
                key = getKey(o), 
                target = [];
            q[key] ??= { value: undefined, target };
            if (q[key].value) {
                q[key].target[0] ??= q[key].value;
                return o;
            }
            q[key].value = o;
            return target;
        })({}))
        .flat();

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • This works fine. what if I am willing to remove specific non duplicate records? E,g those unique records whose type == 'notthisone'. considering following array: const arr = [{id: 1, score: 10}, {id: 1, score: 10}, {id: 3, score: 20}, {id: 4, score: 5}, {id: 77, score: 25, type: 'notthisone'}]; Now here I am expecting following response: [{id: 1, score: 10}, {id: 1, score: 10}, {id: 77, score: 25, type: 'notthisone'}] – StormTrooper Nov 15 '21 at 12:09
  • you could add a check for special objects in advance. please see edit. – Nina Scholz Nov 15 '21 at 12:13
0

You may consider using Array.reduce combined with Array.find (Array.find is less 'expensive' than Array.filter). Something like (also took your question on @Nitheesh' answer into account):

const arr = [
  {id: 1, score: 10}, 
  {id: 1, score: 10}, 
  {id: 3, score: 20}, 
  {id: 4, score: 5}, 
  {id: 77, score: 25, include: true} ];

const log =  document.querySelector(`pre`);
log.textContent = `**Reduced\n${
  JSON.stringify(retrieveDuplicates(arr), null, 2)}`;
log.textContent += `\n\n**Looped\n${
  JSON.stringify(retrieveDuplicatesLoop(arr), null, 2)}`;

// reducer
function retrieveDuplicates(arr) {
  return arr.reduce( ( acc, v, i ) => {
    const exists = v.include ? v :
      arr.slice(i+1).find( vv => v.id === vv.id && v.score === vv.score );
    acc = !acc.length && exists ? acc.concat({...v}) : acc;
    return exists ? [...acc, {...exists}] : acc; 
  }, [] );
}

// Or in a classic loop
function retrieveDuplicatesLoop(arr) {
  let result = [];
  
  for (let i = 0; i < arr.length; i += 1) {
    const exist = arr[i].include ? arr[i] : 
      arr.slice(i+1).find(v => arr[i].id === v.id && arr[i].score === v.score);
    if (!result.length && exist) { result.push(arr[i]); }
    result = exist && result.concat(arr[i]) || result;
  }
  
  return result;
}
<pre></pre>
KooiInc
  • 119,216
  • 31
  • 141
  • 177