Je travaille actuellement sur le développement d'une API, où je dois créer une fonction pour extraire des objets uniques "complètement", c'est-à-dire aucune correspondance d'attribut dans aucun des objets.

J'ai un tableau d'objets de schéma fixe comme:

let arr = [
  {
    "a": "a1",
    "b": "b1",
    "c": "c1",
    "d": "d1",
    "e": "e1"
  },
  {
    "a": "a2",
    "b": "b2",
    "c": "c2",
    "d": "d2",
    "e": "e2"
  },
  {
    "a": "a3",
    "b": "b3",
    "c": "c2", // matching attribute
    "d": "d3",
    "e": "e3"
  },
  // … more objects here, maximum 200 * 33 objects
]

Et je veux le résultat comme:

[
  {
    "a": "a1",
    "b": "b1",
    "c": "c1",
    "d": "d1",
    "e": "e1"
  },
  {
    "a": "a2",
    "b": "b2",
    "c": "c2",
    "d": "d2",
    "e": "e2"
  },
  // … 
]

En cas d'attribut correspondant, l'objet qui est rencontré en premier dans le tableau doit être conservé et l'autre être ignoré. Des propriétés différentes n'auront jamais de valeurs qui se chevauchent.

Mon implémentation actuelle:

for (let i in arr[0]) {
    let map = new Map();
    arr = arr.filter(obj => map.get(obj[i]) ? false : map.set(obj[i], true));
}
console.log(arr);

Existe-t-il un meilleur moyen (plus efficace) de le faire?

2
brc-dd 9 mai 2020 à 14:02

3 réponses

Meilleure réponse

Je ferais un ensemble de toutes les valeurs trouvées jusqu'à présent. Lors de la vérification d'un nouvel objet, vérifiez si l'une de ses valeurs est incluse dans l'ensemble: si c'est le cas, excluez-le, sinon ajoutez toutes les valeurs à l'ensemble:

let arr = [
  {
    "a": "a1",
    "b": "b1",
    "c": "c1",
    "d": "d1",
    "e": "e1"
  },
  {
    "a": "a2",
    "b": "b2",
    "c": "c2",
    "d": "d2",
    "e": "e2"
  },
  {
    "a": "a3",
    "b": "b3",
    "c": "c2", // matching attribute
    "d": "d3",
    "e": "e3"
  },
  // … more objects here, maximum 200 * 33 objects
];
const valuesFound = new Set();
const uniques = arr.filter((obj) => {
  const vals = Object.values(obj);
  if (vals.some(val => valuesFound.has(val))) {
    return false;
  }
  for (const val of vals) {
    valuesFound.add(val);
  }
  return true;
});
console.log(uniques);

Cela a la plus faible complexité de calcul possible - il n'y a pas de boucles imbriquées sauf le .some, et le rappel de .some est O(n), car il utilise Set.prototype.has.

1
CertainPerformance 9 mai 2020 à 11:07

Il devrait être plus rapide de suivre uniquement les valeurs, plutôt que de rechercher des valeurs par clé:

const data = getData()
const cache = {}

const results = data.filter(obj => {
  const added = []
  for (const v of Object.values(obj)){
    if( cache[v] ){
      Object.keys(added).forEach(v=>delete cache[v])
      return false
    }
    else
      added[v] = cache[v] = 1
  }
  return true
});

console.log(results);

/* ===================================================== */
function getData() {
  return [{
      "a": "a1",
      "b": "b1",
      "c": "c1",
      "d": "d1",
      "e": "e1"
    },
    {
      "a": "a2",
      "b": "b2",
      "c": "c2",
      "d": "d2",
      "e": "e2"
    },
    {
      "a": "a3",
      "b": "b3",
      "c": "c2", // matching attribute
      "d": "d3",
      "e": "e3"
    },
    // values in previous object should not affect this unique object:
    {
      "a": "a3",
      "b": "b3",
      "d": "d3",
      "e": "e3"
    },
    // … more objects here, maximum 200 * 33 objects
  ];
}
0
Mike 9 mai 2020 à 11:47

Je pense que c'est plus concis. Au moment où quelque chose retourne false every, le court-circuit sera court.

arr.filter(function(o){
    var that = this;
    return Object.values(o).every(v=>!~that.indexOf(v) && that.push(v))
},[])
0
ibrahim tanyalcin 9 mai 2020 à 11:30