Home > Mobile >  out of multiple occurrences of item, remove only the second one
out of multiple occurrences of item, remove only the second one

Time:07-20

My array looks like this:

{"numb":1, "idSpec":55,}
{"numb":2, "idSpec":42,}
{"numb":3, "idSpec":96,}
{"numb":4, "idSpec":96,}
{"numb":5, "idSpec":96,}
{"numb":6, "idSpec":96,}

You can see that idSpec: 96 has multiple occurrences in my object.

This is my desired output

{"numb":1, "idSpec":55,}
{"numb":2, "idSpec":42,}
{"numb":3, "idSpec":96,}
{"numb":5, "idSpec":96,}
{"numb":6, "idSpec":96,}

How can I only remove idSpec's second occurrence?

CodePudding user response:

What you want to do is iterate over your array, keep track of the already seen specIds and remove the first, second occurency. This could look smh like this:

const seenIds = [];
const removedIds = []; 

array = array.filter(item => {
 // We haven't seen the id yet. add it to the seenIds and return the element
 if (!seenIds.includes(item.specId)) {
   seenIds.push(item.specId)
   return true
 }

 // We have seen the element but havent removed it yet (second occurency) remove the element
 if(!removedIds.includes(item.specId)) {
  removedIds.push(item.specId)
  return false
 }

 // We have seen the Element but its at least the third occurency.
 return true
})

(In this szenario we are looping over array just once so its O(n) Iterations.)

CodePudding user response:

You can do something like this

const data = [{ numb: 1, idSpec: 55 }, { numb: 2, idSpec: 42 }, { numb: 3, idSpec: 96 }, { numb: 4, idSpec: 96 }, { numb: 5, idSpec: 96 }, { numb: 6, idSpec: 96 }];

const removeSecond = (items) => {
   const idSpecMap = {};
   return items
       .map((item) => {
           idSpecMap[item.idSpec] = (idSpecMap[item.idSpec] || 0)   1;
           return {
               ...item,
               count: idSpecMap[item.idSpec],
           };
       })
       .filter((item) => item.count !== 2)
       .map(({ count, ...rest }) => rest);
};

console.log(removeSecond(data));

CodePudding user response:

Below I've composed a function which will help you filter the nth occurrence of something in an array. You can use it on the data in your example, or with any other type of array data that you need to get a filtered version of based on similar criteria (position of occurrence). The code includes comments to explain how it works and tests to prove its effectiveness.

The English description of the logic is that you just use a Map to keep a running count of each occurrence of the value you're interested in comparing (which is determined by a selector function), and filter out the occurrence counts which match the target value:

TS Playground

'use strict';

/**
 * Returns a filtered array, excluding the nth occurrence of a value selected
 * from each element
 *
 * @param array - Unfiltered array
 * @param n - Which occurrence to exclude (first occurrence is 1)
 * @param selector - Function for selecting from each
 * array element the value to compare
 * @returns Filtered array
 */
function filterNthOccurrence (
  array,
  n = 0,
  selector = (value) => value,
) {
  const counts = new Map();
  const filtered = [];

  for (const element of array) {
    const value = selector(element);
    const count = (counts.get(value) ?? 0)   1;
    counts.set(value, count);
    if (count !== n) filtered.push(element);
  }

  return filtered;
}

// Used in the tests below
function areEqualJson (a,  b) {
  return JSON.stringify(a) === JSON.stringify(b);
}

const input = [
  {numb: 1, idSpec: 55},
  {numb: 2, idSpec: 42},
  {numb: 3, idSpec: 96},
  {numb: 4, idSpec: 96},
  {numb: 5, idSpec: 96},
  {numb: 6, idSpec: 96},
];

const expected = [
  {numb: 1, idSpec: 55},
  {numb: 2, idSpec: 42},
  {numb: 3, idSpec: 96},
  {numb: 5, idSpec: 96},
  {numb: 6, idSpec: 96},
];

//                                        filter the 2nd occurrence
//                                        ^  select the "idSpec" prop for comparison
//                                        ^  ^
const actual = filterNthOccurrence(input, 2, o => o.idSpec);
const equal = areEqualJson(actual, expected);
console.log('equal:', equal);


// More tests:
const actual2 = filterNthOccurrence(input, 1, o => o.idSpec);
const expected2 = [
  {numb: 4, idSpec: 96},
  {numb: 5, idSpec: 96},
  {numb: 6, idSpec: 96},
];
const equal2 = areEqualJson(actual2, expected2);
console.log('equal2:', equal2);

const actual3 = filterNthOccurrence(['a', 'b', 'c', 'a', 'b', 'a', 'a'], 2);
const expected3 = ['a', 'b', 'c', 'a', 'a'];
const equal3 = areEqualJson(actual3, expected3);
console.log('equal3:', equal3);

  • Related