0

I am fetching data from an api that, sometimes, gives me multiple objects with the same values, or very similar values, which I want to remove.

For example, I might get back:

  [
   {
    "Name": "blah",
    "Date": "1992-02-18T00:00:00.000Z",
    "Language": "English",
  },
   {
    "Name": "blahzay",
    "Date": "1998-02-18T00:00:00.000Z",
    "Language": "French",
  },       {
    "Name": "blah",                     // same name, no problem
    "Date": "1999-02-18T00:00:00.000Z", // different date
    "Language": "English",             // but same language
  },
 ]

So I want to check that no two objects have a key with the same "Language" value (in this case, "English").

I would like to get the general process of filtering out the entire object if it's "Language" value is duplicated, with the extra issue of not having the same number of objects returned each time. So, allowing for dynamic number of objects in the array.

There is an example here: Unexpeected result when filtering one object array against two other object arrays

but it's assuming that you have a set number of objects in the array and you are only comparing the contents of those same objects each time.

I would be looking for a way to compare

  arrayName[eachObject].Language === "English"

and keep one of the objects but any others (an unknown number of objects) should be filtered out, most probably using .filter() method along with .map().

Kelvin Aitken
  • 433
  • 6
  • 18

4 Answers4

1

You could take a hash table and filter the array by checking Name and Language.

var array = [{ Name: "blah", Date: "1992-02-18T00:00:00.000Z", Language: "English" }, { Name: "blahzay", Date: "1998-02-18T00:00:00.000Z", Language: "French" }, { Name: "blah", Date: "1999-02-18T00:00:00.000Z", Language: "English" }],
    hash = {},
    result = array.filter(({ Name, Language }) => {
        var key = `${Name}|${Language}`;
        if (!hash[key]) return hash[key] = true;
    });

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

The below snippets stores the languages that have been encountered in an array. If the current objects language is in the array then it is filtered out. It makes the assumption that the first object encountered with the language is stored.

const objs = [
   {
    "Name": "blah",
    "Date": "1992-02-18T00:00:00.000Z",
    "Language": "English",
  },
   {
    "Name": "blahzay",
    "Date": "1998-02-18T00:00:00.000Z",
    "Language": "French",
  },       {
    "Name": "blah",                     // same name, no problem
    "Date": "1999-02-18T00:00:00.000Z", // different date
    "Language": "English",             // but same language
  },
 ],
 presentLanguages = [];
 let languageIsNotPresent;
 const objsFilteredByLanguage = objs.filter(function (o) {
  languageIsNotPresent = presentLanguages.indexOf(o.Language) == -1;
  presentLanguages.push(o.Language);
  return languageIsNotPresent;
 });
 console.log(objsFilteredByLanguage);
tstrand66
  • 968
  • 7
  • 11
  • I like this but having trouble understanding what's happening. So it's looping through the objects in the array ("(o)") and for each, checking if it's in the array (languageIsNotPresent is either true or false) then, no matter if the value is true or false, adding it to the presentLanguages array, then returning either true or false to finish the loop. Is the final return nullifying the previous addition to the array? – Kelvin Aitken Feb 19 '20 at 14:45
  • In this case presentLanguages is providing the filter, since the request is to only let one object through per language. the js Array.filter takes a bool return value. If true then the the item is present in the final filtered array if its false then its not. Thus by using indexOf == -1 we only return true if the languageIsNotPresent and that item is added to our final array. So the return doesnt nullify the previous addition because they are 2 separate arrays. 1 acting as a filter and the other a final filtered product. – tstrand66 Feb 19 '20 at 14:58
  • Thank you. It's a very nice, succinct function. – Kelvin Aitken Feb 19 '20 at 18:45
1

Using Set makes it easy to remove duplicates for as many keys as you like. I tried to be as verbose as possible so that each step was clear.

var objects = [{ "Name": "blah", "Date": "1992-02-18T00:00:00.000Z", "Language": "English", }, { "Name": "blah", "Date": "1998-02-18T00:00:00.000Z", "Language": "French", }, { "Name": "blah", "Date": "1999-02-18T00:00:00.000Z", "Language": "English" }];

function uniqueKeyVals(objects, key) {
  const objVals = objects.map(object => object[key]); // ex. ["English", "French", "English"]  
  return objects.slice(0, new Set(objVals).size);     // ex. { "English", "French" }.size = 2
}

function removeKeyDuplicates(objects, keys) {
  keys.forEach(key => objects = uniqueKeyVals(objects, key));
  return objects;
}

// can also use uniqueKeyVals(key) directly for just one key
console.log("Unique 'Language': \n", removeKeyDuplicates(objects, ["Language"]));
console.log("Unique ['Language', 'Name']: \n", removeKeyDuplicates(objects, ["Language", "Name"]));
Lewis
  • 4,285
  • 1
  • 23
  • 36
  • That works. I will run a few tests and see if it stays on it's feet when getting a variety of data back from the api. I will also poke around with the code until I understand completely what's happening with each function. – Kelvin Aitken Feb 18 '20 at 21:32
0

I would use the underscore module for JavaScript and the unique function in this scenario. Here is a sample array of data objects:

let data = [{
    name: 'blah',
    date: Date.now(),
    language: "en"
},
{
    name: 'noblah',
    date: Date.now(),
    language: 'es'
},
{
    name: 'blah',
    date: Date.now(),
    language: 'en'
}];

Then we can use the unique function in the underscore library to only return a copy of the data that has unique values associated with the language key:

const result = _.unique(data, 'language');
Simeon Ikudabo
  • 2,152
  • 1
  • 10
  • 27
  • This answer does return the output the OP is looking for so someone would need to explain that downvote. That's puzzling. I'd welcome anyone to run this code and test it for themselves. – Simeon Ikudabo Feb 18 '20 at 20:54
  • my guess is the down vote stems from 2 things: 1) reliance on a library instead of vanilla js when the op didnt specify a desire for that library 2) you didnt use the data example that the op supplied for your answer. However i agree a down vote without explanation is frustrating. – tstrand66 Feb 18 '20 at 21:42