1

I want to be able to return duplicates except first occurance in an array of objects based of the place and keyword. Both should match and return the documents in an new array. Here is my trial run:

var things = [
                {place: 'hello', keyword: 'hey', id: 0},
                {place: 'hi', id: 1},
                {place: 'hello', keyword: 'hey', id: 2},
                {place: 'hello', keyword: 'man', id: 3}
            ]
var duplicates = [];

things.forEach((item, index) => {
    if(things.indexOf(item.place) != index && things.indexOf(item.keyword) != index) {
        duplicates.push(item);
    }
});

Expected output:

[{place: 'hello', keyword: 'hey', id: 2}]

Any help would be great (without any frameworks, just ES6 or older). Thanks

EDIT: It should match multiple specified values such as keyword and place.

3
  • I would consider using array.find in a loop as it returns the first matching element. Commented Feb 14, 2019 at 7:58
  • Based on your description I would expect {place: 'hello', keyword: 'man', id: 3} to be in the output as well Commented Feb 14, 2019 at 7:59
  • @adelin it should only match keyword and place Commented Feb 14, 2019 at 16:46

3 Answers 3

2

You could count the same keys and filter if the count is greater than one with an object for counting

const
    getKey = o => keys.map(k => o[k]).join('|'),
    keys = ['place', 'keyword'],
    things = [{ place: 'hello', keyword: 'hey', id: 0 }, { place: 'hi', id: 1 }, { place: 'hello', keyword: 'hey', id: 2 }, { place: 'hello', keyword: 'man', id: 3 }],
    hash = Object.create(null),
    duplicates = things.filter(o =>
        (k => (hash[k] = (hash[k] || 0) + 1) > 1)
        (getKey(o))
    );
  
console.log(duplicates);

Sign up to request clarification or add additional context in comments.

4 Comments

The second approach is perfect tbh. Even if OP means index > 1, it's just matter of changing the operator.
I forget to mention in the original question, it should check for place and keyword both.
@ninaScholz its not working as expected as its only returning one occurrence of the duplicated item. See jsfiddle: jsfiddle.net/L2ed1pxt/1
ok, i got it. chencge the counting condition to > 1 in (k => (hash[k] = (hash[k] || 0) + 1) > 1).
0

The obvious solution is that you'll have to track the objects you have seen in order to do it how you want.

const seen = [];
const duplicates = [];

things.forEach(item => {
  const sawItem = seen.find(seenItem => item.place === seenItem.place && item.keyword === seenItem.keyword)
  if (sawItem) {
     duplicates.push(sawItem);
  } else {
    seen.push(sawItem);
  }
});

This isn't a very efficient algorithm however, so I'm curious to see a better way to do it.

2 Comments

This did not work for me, getting error: cannot find place of undefined seen as seen is not defined.
It is defined with const seen = [];?
0

You could group the items based on place and then get the first item from those groups with length > 1

const things = [{ place: 'hello', keyword: 'hey', id: 0 }, { place: 'hi', id: 1 }, { place: 'hello', keyword: 'hey', id: 2 }, { place: 'hello', keyword: 'man', id: 3 }];

const merged = things.reduce((r, a) => {
  (r[a.place] = r[a.place] || []).push(a)
  return r
}, {})

const final = Object.values(merged)
                    .filter(a => a.length > 1)
                    .map(a => a[1])

console.log(final)

6 Comments

result should be id: 2.
@NinaScholz ah, reread the question now. OP wants all the duplicates except the first one or just the duplicate at index 1?
interesting question. but if all except the first duplicate, then the above expected output has to be different. maybe it's just bad wording or the wrong result (which usually is correct).
@NinaScholz you're right. It's the duplicate at index 1 since there is no id: 3 in the expected output.
I forget to mention in the original question, it should check for place and keyword both.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.