0
\$\begingroup\$

I recently had a problem whereby I wanted to filter objects in an array by comparing the values of nested properties of each object to see if there were duplicates.

I have tried to explain as clearly as possible the scenario below and the solution I came up with. I wondering if it's possible to improve this solution and if so, how?

Example:

let articles = [{dateAdded: "31-5-1989", readId: 123, article: { id: 1}}, {dateAdded: "31-5-1989", readId: 124, article: { id: 2}}, {dateAdded: "31-5-1989", readId: 125, article: { id: 1}}]

In this array, you can see each object has a readId and an articleId. The first and last objects in the array have a different readId but a matching articleId. We want to return every object in this array unless we have already returned an object with the same articleId.

Here is my solution:

const removeDuplicatesbyArticleId = articles => {
  let articleIdsArray = [];
  let finalArray = [];

  articleResponse.reading.map(element => {
    if (!articleIdsArray.includes(element.article.id)) {
      articleIdsArray.push(element.article.id);
      finalArray.push(element);
    }
  });

  let filteredArticleResponse = articles;
  filteredArticleResponse.reading = finalArray;
  return filteredArticleResponse;
};

All I'm doing here is looping through the array, checking if the articleId exists in the articleIdsArray and if not, pushing this onto finalArray and then repeating before returning finalArray at the end of the loop.

This solution feels a little crude, I'd be really grateful if anyone could suggest an alternative(s) solution(s) that is easier to read, better for performance, or both!

I apologise if this is a repeat, from much searching I couldn't find a question and answer that matched my specific case.

\$\endgroup\$
0

2 Answers 2

2
\$\begingroup\$

We want to return every object in this array unless we have already returned an object with the same articleId.

I would use a Set object to keep track of which articleId values we've already used and then use .filter() to return only the items that pass the test. Here's a runnable implementation:

let articles = [
  {dateAdded: "31-5-1989", readId: 123, article: { id: 1}}, 
  {dateAdded: "31-5-1989", readId: 124, article: { id: 2}}, 
  {dateAdded: "31-5-1989", readId: 125, article: { id: 1}}
];

function getUniqueArticles(array) {
    let ids = new Set();
    return array.filter(function(item) {
        if (!ids.has(item.article.id)) {
            ids.add(item.article.id);
            return true;
        }
        return false;
    });
}

console.log(getUniqueArticles(articles));


Note: Your articles array does not look valid as there seems to be a missing comma here in each array element and you probably mean to have quotes around the date to make it into a string:

{dateAdded: "31-5-1989", readId: 123, article: { id: 1}}
           ^^^       ^^^           ^^^
\$\endgroup\$
1
  • \$\begingroup\$ thanks this is much cleaner, had seen Set() but for some reason didn't look into it more this is really useful thanks. I was writing the array on the fly and forgot the quotes and commas - added in now, thanks for highlighting. \$\endgroup\$
    – Will Kempster
    Commented Jul 23, 2018 at 21:37
0
\$\begingroup\$

You can use a function that removes the duplicates based on the field you want to filter for example

 function removeDuplicate(arr){
        let unique_array = arr.filter(function(elem, index, self) {
            return index == self.map(res => res.article.id).indexOf(elem.article.id);
        });
        return unique_array
    }
\$\endgroup\$