web-dev-qa-db-ja.com

JavaScriptを使用して配列から重複オブジェクトを削除する

重複しているオブジェクトを配列から削除する効率的な方法を見つけ出し、最も効率的な答えを探しています。インターネットを見回したところ、すべてがプリミティブデータを使用しているように見えます...または大規模な配列に対してスケーラブルではありません。これは私の現在の実装であり、改善することができ、ラベルを避けたいと思っています。

 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);
    };

配列は次のようになります。

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

順序は重要ではありませんが、並べ替えがより効率的になる場合、私は挑戦します...

そして、oを知らない人にとってはラベルであり、新しい配列にプッシュするのではなく、ループに戻るように言っているだけです。

純粋なjavascriptはライブラリを使用しないでください。

ANSWERS SO FAR:

以下の回答のパフォーマンステスト: http://jsperf.com/remove-duplicates-for-loops

19
Lion789

そこにある問題は、複雑さが二乗されていることです。これを行うには、「連想配列」を使用するだけです。

配列を取得してループし、連想配列にキーとして配列の値を追加できます。重複キーは許可されないため、重複は自動的に削除されます。

比較するときにタイトルとアーティストを探しているので、実際には次のようなものを使用することができます。

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

次に、arrResultを再度ループし、配列を再作成します。

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

ポールのコメントを含むように更新。ありがとう!

30
Henrique Feijo

ここに私のために働くソリューションがあります。

ヘルパー関数:

// 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;
}

そして、単に呼び出します:

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

基本的なソート後一意の実装、フィドル [〜#〜] here [〜#〜]

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;
}

最も効率的な場合とそうでない場合があり、完全にスケーラブルでなければなりません。いくつかのconsole.logsを追加したので、動作するように確認できます。

[〜#〜] edit [〜#〜]

関数が使用するスペースを節約するために、最後にforループを実行しましたが、一意の結果のみが適切に検出されなかった可能性が高いようです(単純なjsfiddleテストに合格したことを確認してください)。 forループを次のものに置き換えてみてください。

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

以下は、Henrique Feijoの十分な説明と、カットアンドペーストが可能な例を含む回答です。

目標:重複オブジェクト(このようなもの)を含むオブジェクトの配列を変換します...

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

...重複オブジェクトのないオブジェクトの配列へ(このような):

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

コメントで提供される説明:

    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

この機能を使用します。ソートは行われませんが、結果が生成されます。性能を測定することは決してありません。

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)); //これにより、[1,2,3、 ""、 "a"、 "b"]すべての一意の要素が生成されます。

1
ShiningSandy

以下のコードでは、オブジェクトを文字列形式のJSONと比較し、重複を削除し、単純な配列で正常に動作します。

    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

アンダースコアjsを使用している場合、重複するオブジェクトを簡単に削除できます。 http://underscorejs.org/#uniq

0
Mohammed Safeer

ES6と短いものが好きな人のために、ここに1つの解決策があります。

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)

上記の例は、一意のtitleまたはidに対してのみ機能します。基本的に、タイトルが重複している曲の新しいマップを作成します。

0