web-dev-qa-db-fra.com

Supprimer les objets en double d'un tableau à l'aide de javascript

J'essaie de trouver un moyen efficace de supprimer des objets qui sont des doublons d'un tableau et de chercher la réponse la plus efficace. J'ai regardé autour d'Internet, tout semble utiliser des données primitives ... ou pas évolutif pour les grands tableaux. Ceci est mon implémentation actuelle qui peut être améliorée et je veux essayer d'éviter les étiquettes.

 Test.prototype.unique = function (arr, artist, title, cb) {
        console.log(arr.length);
        var n, y, x, i, r;
        r = [];      
        o: for (i = 0, n = arr.length; i < n; i++) {

          for (x = 0, y = r.length; x < y; x++) {

                if (r[x].artist == arr[i].artist && r[x].title == arr[i].title) {
                    continue o;
                }
            }
            r.Push(arr[i]);
        }

        cb(r);
    };

et le tableau ressemble à ceci:

[{title: sky, artist: jon}, {title: rain, artist: Paul}, ....]

L'ordre n'a pas d'importance, mais si le tri le rend plus efficace, je suis prêt à relever le défi ...

et pour les gens qui ne connaissent pas o, c'est une étiquette et c'est juste dire revenir à la boucle au lieu de pousser vers le nouveau tableau.

Javascript pur s'il vous plaît pas de bibliothèques.

RÉPONSES SO FAR:

Le test de performance pour les réponses ci-dessous: http://jsperf.com/remove-duplicates-for-loops

19
Lion789

Je vois, le problème est que la complexité est au carré. Il y a une astuce pour le faire, c'est simplement en utilisant des "tableaux associatifs".

Vous pouvez obtenir le tableau, le boucler et ajouter la valeur du tableau en tant que clé au tableau associatif. Puisqu'il n'autorise pas les clés dupliquées, vous vous débarrasserez automatiquement des doublons.

Puisque vous recherchez un titre et un artiste lors de la comparaison, vous pouvez réellement essayer d'utiliser quelque chose comme:

var arrResult = {};
for (i = 0, n = arr.length; i < n; i++) {
    var item = arr[i];
    arrResult[ item.title + " - " + item.artist ] = item;
}

Ensuite, vous bouclez à nouveau l'arrResult et recréez le tableau.

var i = 0;
var nonDuplicatedArray = [];    
for(var item in arrResult) {
    nonDuplicatedArray[i++] = arrResult[item];
}

Mis à jour pour inclure le commentaire de Paul. Merci!

30
Henrique Feijo

Voici une solution qui fonctionne pour moi.

Fonctions d'assistance:

// sorts an array of objects according to one field
// call like this: sortObjArray(myArray, "name" );
// it will modify the input array
sortObjArray = function(arr, field) {
    arr.sort(
        function compare(a,b) {
            if (a[field] < b[field])
                return -1;
            if (a[field] > b[field])
                return 1;
            return 0;
        }
    );
}

// call like this: uniqueDishes = removeDuplicatesFromObjArray(dishes, "dishName");
// it will NOT modify the input array
// input array MUST be sorted by the same field (asc or desc doesn't matter)
removeDuplicatesFromObjArray = function(arr, field) {
    var u = [];
    arr.reduce(function (a, b) {
        if (a[field] !== b[field]) u.Push(b);
        return b;
    }, []);
    return u;
}

puis appelez simplement:

        sortObjArray(dishes, "name");
        dishes = removeDuplicatesFromObjArray(dishes, "name");
3
Nico

Implémentation de tri puis unique, violon ICI :

function unique(arr) {
    var comparer = function compareObject(a, b) {
        if (a.title == b.title) {
            if (a.artist < b.artist) {
                return -1;
            } else if (a.artist > b.artist) {
                return 1;
            } else {
                return 0;
            }
        } else {
            if (a.title < b.title) {
                return -1;
            } else {
                return 1;
            }
        }
    }

    arr.sort(comparer);
    console.log("Sorted: " + JSON.stringify(arr));
    for (var i = 0; i < arr.length - 1; ++i) {
        if (comparer(arr[i], arr[i+1]) === 0) {
            arr.splice(i, 1);
            console.log("Splicing: " + JSON.stringify(arr));
        }
    }
    return arr;
}

Il peut ou non être le plus efficace et devrait être entièrement évolutif. J'ai ajouté quelques console.logs pour que vous puissiez le voir comme il fonctionne.

[~ # ~] modifier [~ # ~]

Dans l'intérêt d'économiser sur l'espace utilisé par la fonction, j'ai fait cette boucle for à la fin, mais il semble probable que cela n'a pas trouvé correctement que des résultats uniques (dépsite il passe mon test jsfiddle simple). Veuillez essayer de remplacer ma boucle for par ce qui suit:

var checker;
var uniqueResults = [];
for (var i = 0; i < arr.length; ++i) {
    if (!checker || comparer(checker, arr[i]) != 0) {
        checker = arr[i];
        uniqueResults.Push(checker);
    }
}
return uniqueResults;
2
Scott Mermelstein

Voici la réponse d'Henrique Feijo avec de nombreuses explications et un exemple que vous pouvez couper et coller:

Objectif: convertir un tableau d'objets contenant des objets en double (comme celui-ci) ...

[
    {
        "id": 10620,
        "name": "Things to Print"
    },
    {
        "id": 10620,
        "name": "Things to Print"
    },
    {
        "id": 4334,
        "name": "Interesting"
    }
]

... Dans un tableau d'objets sans objets en double (comme celui-ci):

[
    {
        "id": 10620,
        "name": "Things to Print"
    },
    {
        "id": 4334,
        "name": "Interesting"
    }
]

Explication fournie dans les commentaires:

    var allContent = [{
      "id": 10620,
      "name": "Things to Print"
    }, {
      "id": 10620,
      "name": "Things to Print"
    }, {
      "id": 4334,
      "name": "Interesting"
    }]

     //Put Objects Into As Associative Array. Each key consists of a composite value generated by each set of values from the objects in allContent.
    var noDupeObj = {} //Create an associative array. It will not accept duplicate keys.
    for (i = 0, n = allContent.length; i < n; i++) {
      var item = allContent[i]; //Store each object as a variable. This helps with clarity in the next line.
      noDupeObj[item.id + "|" + item.name] = item; //This is the critical step.
      //Here, you create an object within the associative array that has a key composed of the two values from the original object. 
      // Use a delimiter to not have foo+bar handled like fo+obar
      //Since the associative array will not allow duplicate keys, and the keys are determined by the content, then all duplicate content are removed. 
      //The value assigned to each key is the original object which is along for the ride and used to reconstruct the list in the next step.
    }

     //Recontructs the list with only the unique objects left in the doDupeObj associative array
    var i = 0;
    var nonDuplicatedArray = [];
    for (var item in noDupeObj) {
      nonDuplicatedArray[i++] = noDupeObj[item]; //Populate the array with the values from the noDupeObj.
    }

    console.log(nonDuplicatedArray)
1
user3303554

J'utilise cette fonction. il ne fait aucun tri, mais produit un résultat. Je ne peux pas dire sur la performance comme jamais la mesurer.

var unique = function(a){
    var seen = [], result = [];
    for(var len = a.length, i = len-1; i >= 0; i--){
        if(!seen[a[i]]){
            seen[a[i]] = true;
            result.Push(a[i]);
        }
    }
    return result;
}

var ar = [1,2,3,1,1,1,1,1, "", "", "", "", "a", "b"]; console.log (unique (ar)); // cela produira [1,2,3, "", "a", "b"] tous les éléments uniques.

1
ShiningSandy

Le code ci-dessous compare l'objet avec JSON au format String et supprime les doublons et fonctionne correctement avec les tableaux simples.

    Array.prototype.unique=function(a){
     return function(){
        return this.filter(a)
     }
   }(
   function(a,b,c){
     var tmp=[]; 
     c.forEach(function(el){
        tmp.Push(JSON.stringify(el))
    }); 
    return tmp.indexOf(JSON.stringify(a),b+1)<0
  })
0
sabzapple
function remove_duplicates(objectsArray) {
    var arr = [], collection = []; 
    $.each(objectsArray, function (index, value) {
        if ($.inArray(value.id, arr) == -1) { 
            arr.Push(value.id);
            collection.Push(value);
        }
    });
    return collection;
}
0
Raja Maragani

Si vous utilisez le soulignement js, il est facile de supprimer un objet en double. http://underscorejs.org/#uniq

0
Mohammed Safeer

Pour ceux qui aiment ES6 et les trucs courts, voici une solution:

const arr = [
  { title: "sky", artist: "Jon" },
  { title: "rain", artist: "Paul" },
  { title: "sky", artist: "Jon" }
];

Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
const arr = [
  { title: "sky", artist: "Jon" },
  { title: "rain", artist: "Paul" },
  { title: "sky", artist: "Jon" },
  { title: "rain", artist: "Jon" },
  { title: "cry", artist: "Jon" }
];

const unique = Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());

console.log(`New array length: ${unique.length}`)

console.log(unique)

L'exemple ci-dessus ne fonctionne que pour un title ou id unique. Fondamentalement, il crée une nouvelle carte pour les chansons avec des titres en double.

0
Abraham Hernandez