Pertanyaan Hapus nilai duplikat dari larik JS [duplikat]


Pertanyaan ini sudah memiliki jawaban di sini:

Saya memiliki array JavaScript yang sangat sederhana yang mungkin atau tidak mengandung duplikat.

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

Saya perlu menghapus duplikat dan menempatkan nilai-nilai unik dalam array baru.

Saya bisa menunjukkan semua kode yang saya coba tetapi saya pikir itu tidak berguna karena tidak berfungsi. Saya menerima solusi jQuery juga.

Pertanyaan serupa:


861
2018-02-10 14:53


asal


Jawaban:


Cepat dan kotor menggunakan jQuery:

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [];
$.each(names, function(i, el){
    if($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});

362
2018-02-10 15:13



"Pintar" tapi cara naif

uniqueArray = a.filter(function(item, pos) {
    return a.indexOf(item) == pos;
})

Pada dasarnya, kita mengulang di atas array dan, untuk setiap elemen, periksa apakah posisi pertama elemen ini dalam array sama dengan posisi saat ini. Tentunya, dua posisi ini berbeda untuk elemen duplikat.

Menggunakan parameter ke-3 ("array ini") dari callback filter kita dapat menghindari penutupan variabel array:

uniqueArray = a.filter(function(item, pos, self) {
    return self.indexOf(item) == pos;
})

Meskipun ringkas, algoritma ini tidak terlalu efisien untuk array besar (waktu kuadrat).

Hashtable untuk diselamatkan

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

Ini adalah cara yang biasanya dilakukan. Idenya adalah menempatkan setiap elemen dalam hashtable dan kemudian memeriksa kehadirannya secara instan. Ini memberi kita waktu linear, tetapi setidaknya memiliki dua kelemahan:

  • karena kunci hash hanya dapat berupa string dalam Javascript, kode ini tidak membedakan angka dan "string numerik". Itu adalah, uniq([1,"1"]) akan kembali dengan adil [1]
  • untuk alasan yang sama, semua objek akan dianggap sama: uniq([{foo:1},{foo:2}]) akan kembali dengan adil [{foo:1}].

Yang mengatakan, jika array Anda hanya berisi primitif dan Anda tidak peduli tentang jenis (mis. Itu selalu angka), solusi ini optimal.

Yang terbaik dari dua dunia

Solusi universal menggabungkan kedua pendekatan: menggunakan pencarian hash untuk primitif dan pencarian objek secara linear.

function uniq(a) {
    var prims = {"boolean":{}, "number":{}, "string":{}}, objs = [];

    return a.filter(function(item) {
        var type = typeof item;
        if(type in prims)
            return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
        else
            return objs.indexOf(item) >= 0 ? false : objs.push(item);
    });
}

sortir | uniq

Pilihan lainnya adalah mengurutkan array pertama, dan kemudian menghapus setiap elemen yang sama dengan yang sebelumnya:

function uniq(a) {
    return a.sort().filter(function(item, pos, ary) {
        return !pos || item != ary[pos - 1];
    })
}

Sekali lagi, ini tidak bekerja dengan objek (karena semua objek sama untuk sort). Selain itu, kami diam-diam mengubah array asli sebagai efek samping - tidak bagus! Namun, jika masukan Anda sudah diurutkan, ini adalah cara untuk pergi (cukup hapus sort dari atas).

Unik oleh ...

Kadang-kadang diinginkan untuk mengunifikasi daftar berdasarkan beberapa kriteria selain hanya kesetaraan, misalnya, untuk menyaring objek yang berbeda, tetapi berbagi beberapa properti. Ini dapat dilakukan secara elegan dengan melewati callback. Callback "kunci" ini diterapkan ke setiap elemen, dan elemen dengan "kunci" yang sama dihapus. Sejak key diharapkan mengembalikan tabel primitif, hash akan berfungsi dengan baik di sini:

function uniqBy(a, key) {
    var seen = {};
    return a.filter(function(item) {
        var k = key(item);
        return seen.hasOwnProperty(k) ? false : (seen[k] = true);
    })
}

Sangat berguna key() aku s JSON.stringify yang akan menghapus objek yang berbeda secara fisik, tetapi "terlihat" sama:

a = [[1,2,3], [4,5,6], [1,2,3]]
b = uniqBy(a, JSON.stringify)
console.log(b) // [[1,2,3], [4,5,6]]

Jika itu key tidak primitif, Anda harus menggunakan pencarian linier:

function uniqBy(a, key) {
    var index = [];
    return a.filter(function (item) {
        var k = key(item);
        return index.indexOf(k) >= 0 ? false : index.push(k);
    });
}

atau gunakan Set objek di ES6:

function uniqBy(a, key) {
    var seen = new Set();
    return a.filter(item => {
        var k = key(item);
        return seen.has(k) ? false : seen.add(k);
    });
}

(Sebagian orang lebih suka !seen.has(k) && seen.add(k) dari pada seen.has(k) ? false : seen.add(k)).

Perpustakaan

Kedua menggarisbawahi dan Lo-Dash menyediakan uniq metode. Algoritme mereka pada dasarnya mirip dengan cuplikan pertama di atas dan diringkas ke ini:

var result = [];
a.forEach(function(item) {
     if(result.indexOf(item) < 0) {
         result.push(item);
     }
});

Ini kuadrat, tetapi ada barang tambahan yang bagus, seperti membungkus asli indexOf, kemampuan uniqify oleh kunci (iteratee dalam bahasa mereka), dan optimasi untuk array yang sudah diurutkan.

Jika Anda menggunakan jQuery dan tidak tahan apa pun tanpa dolar sebelumnya, itu seperti ini:

  $.uniqArray = function(a) {
        return $.grep(a, function(item, pos) {
            return $.inArray(item, a) === pos;
        });
  }

yang, sekali lagi, variasi dari potongan pertama.

Kinerja

Fungsi panggilan mahal dalam Javascript, oleh karena itu solusi di atas, sesingkat apa pun, tidak terlalu efisien. Untuk kinerja maksimal, ganti filter dengan satu lingkaran dan menyingkirkan panggilan fungsi lainnya:

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

Potongan kode jelek ini sama dengan potongan # 3 di atas, tetapi urutan besarnya lebih cepat (pada 2017 itu hanya dua kali lebih cepat - orang-orang inti JS melakukan pekerjaan yang hebat!)

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

/////

var r = [0,1,2,3,4,5,6,7,8,9],
    a = [],
    LEN = 1000,
    LOOPS = 1000;

while(LEN--)
    a = a.concat(r);

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq(a);
document.write('<br>uniq, ms/loop: ' + (new Date() - d)/LOOPS)

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq_fast(a);
document.write('<br>uniq_fast, ms/loop: ' + (new Date() - d)/LOOPS)

ES6

ES6 menyediakan Set objek, yang membuat semuanya jauh lebih mudah:

function uniq(a) {
   return Array.from(new Set(a));
}

atau

let uniq = a => [...new Set(a)];

Perhatikan bahwa, tidak seperti di python, set ES6 diulang dalam urutan penyisipan, jadi kode ini mempertahankan urutan dari array asli.

Namun, jika Anda memerlukan larik dengan elemen unik, mengapa tidak menggunakan set langsung dari awal?

Generator

Versi generator berbasis "malas" uniq dapat dibangun atas dasar yang sama:

  • ambil nilai selanjutnya dari argumen
  • jika sudah terlihat, lewati saja
  • jika tidak, hasilkan dan tambahkan ke kumpulan nilai yang sudah terlihat

function* uniqIter(a) {
    let seen = new Set();

    for (let x of a) {
        if (!seen.has(x)) {
            seen.add(x);
            yield x;
        }
    }
}

// example:

function* randomsBelow(limit) {
    while (1)
        yield Math.floor(Math.random() * limit);
}

// note that randomsBelow is endless

count = 20;
limit = 30;

for (let r of uniqIter(randomsBelow(limit))) {
    console.log(r);
    if (--count === 0)
        break
}

// exercise for the reader: what happens if we set `limit` less than `count` and why


2209
2018-02-10 15:05



Bosan melihat semua contoh buruk dengan for-loops atau jQuery. Javascript memiliki alat yang sempurna untuk saat ini: sortir, petakan, dan kurangi.

Uniq mengurangi sambil menjaga pesanan yang ada

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

var uniq = names.reduce(function(a,b){
    if (a.indexOf(b) < 0 ) a.push(b);
    return a;
  },[]);

console.log(uniq, names) // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

// one liner
return names.reduce(function(a,b){if(a.indexOf(b)<0)a.push(b);return a;},[]);

Uniq lebih cepat dengan penyortiran

Mungkin ada cara yang lebih cepat tetapi yang ini cukup lumayan.

var uniq = names.slice() // slice makes copy of array before sorting it
  .sort(function(a,b){
    return a > b;
  })
  .reduce(function(a,b){
    if (a.slice(-1)[0] !== b) a.push(b); // slice(-1)[0] means last item in array without removing it (like .pop())
    return a;
  },[]); // this empty array becomes the starting value for a

// one liner
return names.slice().sort(function(a,b){return a > b}).reduce(function(a,b){if (a.slice(-1)[0] !== b) a.push(b);return a;},[]);

Perbarui 2015: Versi ES6:

Di ES6 Anda memiliki Set dan Spread yang membuatnya sangat mudah dan berkinerja untuk menghapus semua duplikat:

var uniq = [ ...new Set(names) ]; // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

Urutkan berdasarkan kejadian:

Seseorang bertanya tentang memesan hasil berdasarkan berapa banyak nama unik yang ada:

var names = ['Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl']

var uniq = names
  .map((name) => {
    return {count: 1, name: name}
  })
  .reduce((a, b) => {
    a[b.name] = (a[b.name] || 0) + b.count
    return a
  }, {})

var sorted = Object.keys(uniq).sort((a, b) => uniq[a] < uniq[b])

console.log(sorted)

266
2018-04-07 22:42



Vanilla JS: Hapus duplikat menggunakan Object seperti Set

Anda selalu dapat mencoba memasukkannya ke objek, lalu mengulangi melalui kunci-kunci:

function remove_duplicates(arr) {
    var obj = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        obj[arr[i]] = true;
    }
    for (var key in obj) {
        ret_arr.push(key);
    }
    return ret_arr;
}

Vanilla JS: Hapus duplikat dengan melacak nilai yang sudah dilihat (aman pesanan)

Atau, untuk versi aman-pesanan, gunakan objek untuk menyimpan semua nilai yang dilihat sebelumnya, dan periksa nilainya sebelum sebelum menambahkan ke array.

function remove_duplicates_safe(arr) {
    var seen = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        if (!(arr[i] in seen)) {
            ret_arr.push(arr[i]);
            seen[arr[i]] = true;
        }
    }
    return ret_arr;

}

ECMAScript 6: Gunakan struktur data Set baru (aman pesanan)

ECMAScript 6 menambahkan yang baru Set Struktur Data, yang memungkinkan Anda menyimpan nilai dari jenis apa pun. Set.values mengembalikan elemen dalam urutan penyisipan.

function remove_duplicates_es6(arr) {
    let s = new Set(arr);
    let it = s.values();
    return Array.from(it);
}

Contoh penggunaan:

a = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

b = remove_duplicates(a);
// b:
// ["Adam", "Carl", "Jenny", "Matt", "Mike", "Nancy"]

c = remove_duplicates_safe(a);
// c:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

d = remove_duplicates_es6(a);
// d:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

73
2018-02-10 15:03



Menggunakan Underscore.js

Ini adalah perpustakaan dengan sejumlah fungsi untuk memanipulasi array.

Ini adalah dasi untuk pergi bersama dengan tuksedo jQuery, dan Backbone.js   bretel.

_.uniq

_.uniq(array, [isSorted], [iterator])  Alias:  unik
  Menghasilkan versi gratis duplikat dari larik, menggunakan === untuk menguji objek   persamaan. Jika Anda tahu sebelumnya bahwa larik disortir, lewat    benar untuk isSorted akan menjalankan algoritma yang jauh lebih cepat. Jika Anda menghendaki   hitung item unik berdasarkan transformasi, berikan iterator   fungsi.

Contoh

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

alert(_.uniq(names, false));

catatan: Lo-Dash (sebuah menggarisbawahi pesaing) juga menawarkan yang sebanding .uniq pelaksanaan.


68
2018-06-30 03:07



Versi satu baris menggunakan filter array dan fungsi indexOf:

arr = arr.filter (function (value, index, array) { 
    return array.indexOf (value) == index;
});

60
2018-02-11 21:18



Anda dapat melakukannya dalam JavaScript, dengan bantuan parameter indeks kedua dari filter metode:

var a = [2,3,4,5,5,4];
a.filter(function(value, index){ return a.indexOf(value) == index });

atau dengan tangan pendek

a.filter((v,i) => a.indexOf(v) == i)

46
2018-06-15 11:05



Cara paling ringkas untuk menghapus duplikat dari larik menggunakan fungsi javascript asli adalah menggunakan urutan seperti di bawah ini:

vals.sort().reduce(function(a, b){ if (b != a[0]) a.unshift(b); return a }, [])

tidak perlu slice maupun indexOf dalam fungsi mengurangi, seperti yang pernah saya lihat dalam contoh lain! masuk akal untuk menggunakannya bersama dengan fungsi filter meskipun:

vals.filter(function(v, i, a){ return i == a.indexOf(v) })

Namun cara lain ES6 (2015) untuk melakukan hal ini yang sudah berfungsi pada beberapa browser adalah:

Array.from(new Set(vals))

atau bahkan menggunakan operator menyebar:

[...new Set(vals)]

tepuk tangan!


28
2017-09-11 23:44



Satu baris:

let names = ['Mike','Matt','Nancy','Adam','Jenny','Nancy','Carl', 'Nancy'];
let dup = [...new Set(names)];
console.log(dup);

28
2017-08-01 01:39