4

I have an array of names which all have duplicates.

let arr = ['John', 'Jack', 'John', 'Jack', 'Jack', 'June', 'June'];

I want to create a new array with some of the duplicate elements, specifying an instance at which they occur.

For example, I may want the array to contain only the second occurrence of John, Jack and June. The array will look like this:

'John', 'Jack', 'June'

To accomplish this, I have declared an object ni and looped through arr to add properties to the object corresponding to each name; the property will hold the value of an array containing each index at which the name occurs.

let ni  = {};

for(let i = 0; i < arr.length; i++) {
  let name = arr[i];
  if(nc.hasOwnProperty(name)) {
    ni[name].push(i);
  } else {
    ni[name] = [i];
  }
}

console.log(ni);
// > Object { John: Array [0, 2], Jack: Array [1, 3, 4], June: Array [5, 6] }

In my Array.filter function, I check if the element's index is equal to index 1 of the object property corresponding to the same name.

let newArr = arr.filter(function(name) {
  if(ni.hasOwnProperty(name)) {
    return arr.indexOf(name) === ni[name][1];
  }
});

This should return John at index 2, Jack at index 3, and June at index 6 to the new array.

However, this has not worked. Logging newArr to the console will output an array completely unchanged from the original.

> Array ["John", "Jack", "John", "Jack", "Jack", "June", "June"]
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
Gerard Way
  • 91
  • 9
  • 2
    So, basically return names with more than one occurrence? – adiga Mar 02 '19 at 11:40
  • Contrary to what you claim, `newArr` as returned by the filter, is empty. – trincot Mar 02 '19 at 11:45
  • Possible duplicate of [Get all unique values in a JavaScript array (remove duplicates)](https://stackoverflow.com/questions/1960473/get-all-unique-values-in-a-javascript-array-remove-duplicates) – Jack Templeman Mar 02 '19 at 11:46

3 Answers3

2

You can use some simple array logic like so:

let newArr = arr.filter((e, i) => i == ni[e][1]);
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
  • @trincot In case the array does not contain enough elements - so if it's undefined, it will take the last element of the array instead. – Jack Bashford Mar 02 '19 at 11:53
  • But that will always be truthy (note that `==` has precedence over `||`) for non-empty strings. Secondly, it defeats the purpose, because `length` could be 1 and then you get the first occurrence. OP wants the second... – trincot Mar 02 '19 at 11:56
  • So I should remove the `||` part? – Jack Bashford Mar 02 '19 at 11:58
  • Yes, I was going to answer at the same time you did, but only with that difference, so better you have it without it. – trincot Mar 02 '19 at 11:59
  • Ok thanks for the feedback @trincot I'm still refining my knowledge on precedence, I'll keep what you mentioned in mind. – Jack Bashford Mar 02 '19 at 12:00
2

You can use reduce to count the occurrence of each name. If the name's count is 2, then push to another array. This way you don't have to run nested array methods:

const array = ['Jane', 'John', 'Jack', 'John', 'Jack', 'Jack', 'June', 'June'];
const filtered = [];
const count = array.reduce((a, name) => {
  a[name] = a[name] + 1 || 1;
  if(a[name] === 2)
    filtered.push(name)
  
  return a;
}, {});

console.log(filtered)

Or get the keys of the counter with count > 1

const array = ['Jane', 'John', 'Jack', 'John', 'Jack', 'Jack', 'June', 'June'],
      count = array.reduce((a, n) => (a[n] = a[n] + 1 || 1, a), {}),
      filtered = Object.keys(count).filter(n => count[n] > 1);
      
console.log(filtered)
adiga
  • 34,372
  • 9
  • 61
  • 83
1

I'm creating an object instead of an array because it seems more suitable to map from name to the index of the 2nd appearance. It works like this: indexOf returns the index of the 1st occurrence of an element in the array so if indexOf is not equal to the current index then it's 2nd or later occurrence, then I check whether the name is not already in the return object so I know it's not 3rd or later occurrence.

let arr = ['John', 'Jack', 'John', 'Jack', 'Jack', 'June', 'June'];
let newObj = arr.reduce(function(acc, cur, idx) {
  if (arr.indexOf(cur)!==idx && !acc[cur]) { acc[cur] = idx; }
 return acc;
}, {});
console.log(newObj);
Dmitry
  • 6,716
  • 14
  • 37
  • 39