Các cách tạo Tập hợp trong JavaScript?


82

Trong Eloquent JavaScript, Chương 4, một bộ giá trị được tạo bằng cách tạo một đối tượng và lưu trữ các giá trị dưới dạng tên thuộc tính, gán giá trị tùy ý (ví dụ: true) làm giá trị thuộc tính. Để kiểm tra xem giá trị đã được chứa trong tập hợp chưa, intoán tử được sử dụng:

var set = {};

if (!'Tom' in set) { 
  set.Tom = true;
}

Đây có phải là JavaScript thành ngữ không? Sử dụng một mảng sẽ không tốt hơn sao?

var set = [];

if (!'Tom' in set) { 
  set.push = 'Tom';
}

1
Bạn sẽ mong đợi một mảng 'Tom' in settrông như thế nào? Có vẻ như bạn đã giả định sai về điều gì đó và tôi đang cố gắng tìm hiểu về điều đó .

10
FYI, bạn cần parens : if(!('Tom' in set)). Hiện tại nó có nghĩa là false in setkể từ khi !'Tom' === false.
pimvdb

ES6 có các bộ, hãy xem câu trả lời của John bên dưới
Ben Taliadoros

Câu trả lời:


101

Các bộ hiện có sẵn trong ES2015 (hay còn gọi là ES6, tức là ECMAScript 6). ES6 là tiêu chuẩn hiện tại cho JavaScript kể từ tháng 6 năm 2015.

ECMAScript 6 có cấu trúc dữ liệu Set hoạt động với các giá trị tùy ý, nhanh và xử lý NaN một cách chính xác. - Axel Rauschmayer , Khám phá ES6

Hai ví dụ đầu tiên từ Axel Rauschmayer của cuốn sách Khám phá ES6 :

Quản lý các yếu tố đơn lẻ:

> let set = new Set();
> set.add('red')

> set.has('red')
true
> set.delete('red')
true
> set.has('red')
false

Xác định kích thước của một Tập hợp và xóa nó:

> let set = new Set();
> set.add('red')
> set.add('green')

> set.size
2
> set.clear();
> set.size
0

Tôi sẽ xem Khám phá ES6 nếu bạn muốn tìm hiểu thêm về Bộ trong JavaScript. Cuốn sách được đọc trực tuyến miễn phí, nhưng nếu bạn muốn ủng hộ tác giả Tiến sĩ Axel Rauschmayer, bạn có thể mua cuốn sách với giá khoảng 30 đô la.

Nếu bạn muốn sử dụng Sets và ES6 bây giờ, bạn có thể sử dụng Babel , bộ chuyển tiếp ES6 đến ES5 và các polyfills của nó.

Chỉnh sửa: Kể từ ngày 6 tháng 6 năm 2017, hầu hết các trình duyệt chính đều có hỗ trợ Bộ hoàn chỉnh trong các phiên bản mới nhất của chúng (ngoại trừ IE 11). Điều này có nghĩa là bạn có thể không cần babel nếu bạn không muốn hỗ trợ các trình duyệt cũ hơn. Nếu bạn muốn xem khả năng tương thích trong các trình duyệt khác nhau bao gồm cả trình duyệt hiện tại của bạn, hãy kiểm tra bảng tương thích ES6 của Kangax .

BIÊN TẬP:

Chỉ cần làm rõ về khởi tạo. Các tập hợp có thể nhận bất kỳ khả năng lặp đồng bộ nào trong phương thức khởi tạo của chúng. Điều này có nghĩa là họ có thể lấy không chỉ mảng mà còn cả chuỗi và trình vòng lặp. Lấy ví dụ về khởi tạo mảng và chuỗi sau của một tập hợp:

const set1 = new Set(['a','a','b','b','c','c']);
console.log(...set1);
console.log(set1.size);
const set2 = new Set("aabbcc");
console.log(...set2);
console.log(set2.size);

Cả hai đầu ra của mảng và chuỗi đều giống nhau. Lưu ý rằng đó ...set1cú pháp lây lan . Có vẻ như mỗi phần tử của có thể lặp lại được thêm từng phần tử một vào tập hợp, do đó, cả mảng và chuỗi đều có các phần tử giống nhau và vì các phần tử có cùng thứ tự nên tập hợp được tạo giống nhau. Một điều khác cần lưu ý về tập hợp là khi lặp lại chúng, thứ tự lặp tuân theo thứ tự mà các phần tử đã được chèn vào tập hợp. Đây là một ví dụ về việc lặp lại trên một tập hợp:

const set1 = new Set(['a','a','b','b','c','c']);
for(const element of set1) {
  console.log(element);
}

Vì bạn có thể sử dụng bất kỳ tệp nào có thể lặp lại để khởi tạo một tập hợp, bạn thậm chí có thể sử dụng một trình lặp từ một hàm trình tạo . Dưới đây là hai ví dụ về khởi tạo trình lặp tạo ra cùng một đầu ra:

// a simple generator example
function* getLetters1 () {
  yield 'a';
  yield 'a';
  yield 'b';
  yield 'b';
  yield 'c';
  yield 'c';
}

// a somewhat more commonplace generator example
// with the same output as getLetters1.
function* getLetters2 (letters, repeatTimes) {
  for(const letter of letters) {
    for(let i = 0; i < repeatTimes; ++i) { 
      yield letter;
    }
  }
}

console.log("------ getLetters1 ------");
console.log(...getLetters1());
const set3 = new Set(getLetters1());
console.log(...set3);
console.log(set3.size);

console.log("------ getLetters2 ------");
console.log(...getLetters2('abc', 2));
const set4 = new Set(getLetters2('abc', 2));
console.log(...set4);
console.log(set4.size);

Các hàm trình tạo của các ví dụ này chỉ có thể được viết để không lặp lại, nhưng nếu hàm trình tạo phức tạp hơn và miễn là điều sau không ảnh hưởng quá tiêu cực đến hiệu suất, bạn có thể sử dụng phương pháp Đặt để giúp chỉ nhận các giá trị từ trình tạo không không lặp lại.

Nếu bạn muốn biết thêm về các bộ mà không cần đọc chương sách của Tiến sĩ Rauschmayer, bạn có thể xem các tài liệu MDN trên Set . MDN cũng có nhiều ví dụ về iterating qua một bộ như sử dụng forEachvà sử dụng .keys, .values.entriesphương pháp. MDN cũng có các ví dụ như tập hợp liên hiệp, giao điểm tập hợp, chênh lệch tập hợp, chênh lệch tập hợp đối xứng và kiểm tra tập hợp siêu hạng. Hy vọng rằng hầu hết các hoạt động đó sẽ có sẵn trong JavaScript mà không cần phải xây dựng các hàm của riêng bạn hỗ trợ chúng. Trên thực tế, có đề xuất TC39 này cho các phương pháp Đặt mới , hy vọng sẽ thêm các phương pháp sau vào Đặt trong JavaScript vào một thời điểm nào đó trong tương lai nếu đề xuất đạt đến giai đoạn 4:

  • Set.prototype.intersection (có thể lặp lại) - phương thức tạo phiên bản Set mới bằng thao tác đặt giao điểm.
  • Set.prototype.union (có thể lặp lại) - phương thức tạo cá thể Set mới bằng hoạt động liên hiệp thiết lập.
  • Set.prototype.difference (có thể lặp lại) - phương thức tạo Tập hợp mới mà không có phần tử hiện diện trong có thể lặp lại.
  • Set.prototype.symmetricDifference (có thể lặp lại) - trả về Tập hợp các phần tử chỉ được tìm thấy trong điều này hoặc trong có thể lặp lại.
  • Set.prototype.isSubsetOf (có thể lặp lại)
  • Set.prototype.isDisjointFrom (có thể lặp lại)
  • Set.prototype.isSupersetOf (có thể lặp lại)

Ngoài ra, bản thân các Set có thể lặp lại, vì vậy bạn có thể khởi tạo các tập hợp với các tập hợp khác để tạo bản sao hoặc thực hiện một cái gì đó như Set mới ([... setA, ... setB]) cho một phép toán hợp nhất.
John

1
@LanceKind Tôi đã thêm một số thông tin bổ sung bao gồm cho thấy rằng bạn có thể khởi tạo bộ với bất kỳ mảng nào có thể lặp lại không chỉ mảng.
John

32

Tôi sử dụng các đối tượng dict làm tập hợp. Điều này hoạt động với chuỗi và số, nhưng tôi cho rằng sẽ gây ra sự cố nếu bạn muốn có một tập hợp các đối tượng sử dụng các toán tử so sánh và bình đẳng tùy chỉnh:

Tạo một tập hợp:

var example_set = 
{
    'a':true,
    'b':true,
    'c':true
}

Thử nghiệm để đưa vào một tập hợp

if( example_set['a'] ){
    alert('"a" is in set');
}

Thêm một phần tử vào một tập hợp

example_set['d'] = true;

Xóa một phần tử khỏi một tập hợp

delete example_set['a'];


1
Mã mà tôi đang làm việc đang sử dụng phiên bản cũ hơn của công cụ không có hỗ trợ Set. Điều này đã giúp.
Abhijith Madhav

16

Bộ không cho phép các mục trùng lặp và thường không đảm bảo thứ tự xác định trước. Mảng thực hiện cả hai điều này, do đó vi phạm ý nghĩa của việc trở thành một tập hợp (trừ khi bạn thực hiện kiểm tra bổ sung).


2
Mảng không lọc các mục trùng lặp ... ví dụ arr.push ({id: 1, tên: "Jake"}) như là NSSet trong Objective-C :)
iTux

Nếu bạn lấy nó một cách nhanh chóng, không. Nhưng bạn chỉ có thể sử dụng id làm khóa mảng (bản đồ). arr[id] = {"name": "Jake"};
Buffalo

11

Cách đầu tiên là JavaScript thành ngữ.

Bất kỳ lúc nào bạn muốn lưu trữ một cặp khóa / giá trị, bạn phải sử dụng một đối tượng JavaScript. Đối với mảng, có một số vấn đề:

  1. Chỉ số là một giá trị số.

  2. Không có cách nào dễ dàng để kiểm tra xem một giá trị có nằm trong một mảng hay không mà không lặp lại.

  3. Một tập hợp không cho phép trùng lặp. Một mảng không.


Điều gì sẽ là tốt nhất để gán cho giá trị tài sản?
helpermethod

1
Có thể loại bỏ các phần tử trùng lặp khỏi mảng JavaScript. stackoverflow.com/a/12166248/975097
Anderson Green,

9

Nếu bạn muốn tạo một tập hợp từ một mảng, chỉ cần thực hiện:

let arr = [1, 1, 2, 1, 3];
let mySet = new Set(arr); // Set { 1, 2, 3 }

Đây là một cú pháp đường mà tôi khá yêu thích khi lập trình bằng Python, rất vui vì ES6 cuối cùng đã làm được điều tương tự.

LƯU Ý: sau đó tôi nhận ra những gì tôi nói không trực tiếp trả lời câu hỏi của bạn. Lý do bạn có "hack" này trong ES5 là vì thời gian tra cứu trong một đối tượng bằng các phím nhanh hơn đáng kể (O (1)) so với trong một mảng (O (n)). Trong các ứng dụng quan trọng về hiệu suất, bạn có thể hy sinh chút khả năng đọc hoặc trực giác này để có hiệu suất tốt hơn.

Nhưng này, chào mừng bạn đến với năm 2017, nơi bạn có thể sử dụng Set thích hợp trong tất cả các trình duyệt hiện đại chính ngay bây giờ!


6

Đặt trong ES6/ ES2015:

ES6/ ES2015bây giờ đã được xây dựng trong bộ. Tập hợp là cấu trúc dữ liệu cho phép lưu trữ các giá trị duy nhất của bất kỳ kiểu nào, cho dù đây là giá trị nguyên thủy hay tham chiếu đối tượng. Một tập hợp có thể được khai báo bằng cách sử dụng phương thức khởi ES6tạo tập hợp có sẵn theo cách sau:

const set = new Set([1, 2, 3, 4, 5]);

Khi tạo một tập hợp bằng cách sử dụng phương thức khởi dựng Set, đối tượng tập hợp mới tạo của chúng ta sẽ kế thừa từ Set.prototype. Điều này có tất cả các loại phương thức và thuộc tính bổ trợ. Điều này cho phép bạn dễ dàng thực hiện những việc sau:

Thí dụ:

const set = new Set([1, 2, 3, 4, 5]);

// checkout the size of the set
console.log('size is: ' + set.size);

// has method returns a boolean, true if the item is in the set
console.log(set.has(1));

// add a number
set.add(6);

// delete a number
set.delete(1);

// iterate over each element using a callback
set.forEach((el) => {
  console.log(el);
});

// remove all the entries from the set
set.clear();

Tính tương thích của trình duyệt web:

Tất cả các trình duyệt chính hiện hỗ trợ đầy đủ các bộ ngoại trừ IE, nơi thiếu một số tính năng. Để tham khảo chính xác, vui lòng tham khảo tài liệu mdn .


@Velojet: Ok, đủ công bằng.
kjhughes

3

Có hai vấn đề khi sử dụng các đối tượng javascript trần để mô phỏng các tập hợp: thứ nhất, một đối tượng có thể có một thuộc tính kế thừa sẽ làm hỏng toán tử "in" và thứ hai, bạn chỉ có thể lưu trữ các giá trị vô hướng theo cách này, tạo một tập hợp các đối tượng không khả thi. Do đó, việc triển khai thực tế các Bộ nên cung cấp các phương thức addcontainsthay vì các inphép gán đơn giản và thuộc tính.


@kojiro: đối tượng được chuyển đổi thành chuỗi khi được sử dụng như phím:set={};set[{x:1}]=123;alert(set[{z:99}])
Georg

3

Bạn có thể thử Buckets , là một thư viện cấu trúc dữ liệu javascript và có mọi thứ bạn cần để thao tác các bộ.


nó có cung cấp phương thức addAll (mảng) không?
jorrebor

1

Cơ bản tạo và sử dụng đối tượng Set 🔷

let mySet = new Set()

mySet.add(2)         // Set {2}
mySet.add(7)         // Set {2, 7}
mySet.add(7)         // Set {2, 7}
mySet.add('my text') // Set {2, 7, 'my text'}
let myObj = { a: 1, b: 2 }
mySet.add(myObj)     // Set {2, 7, 'my text', {...}}
mySet.has(2)         // true
mySet.has(myObj)     // true
mySet.size           // 4

Lặp lại

for (let item of mySet) console.log(item)  // 2, 7, 'my text', {a:1, b:2}
mySet.forEach(value => console.log(value)) // 2, 7, 'my text', {a:1, b:2}

Chuyển đổi sang mảng

var myArr = Array.from(mySet)             // [2, 7, 'my text', {a:1, b:2}]

❕ Tính năng khác biệt nhất mà Set cung cấp là mọi giá trị trong đối tượng Set phải là duy nhất. Vì vậy, bạn không thể thêm các giá trị trùng lặp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.