Tìm kiếm trang web

Cách ánh xạ/giảm/lọc một Set trong JavaScript?


Có cách nào để map/reduce/filter/v.v. một Set trong JavaScript không hay tôi sẽ phải tự viết?

Đây là một số phần mở rộng Set.prototype hợp lý

Set.prototype.map = function map(f) {
  var newSet = new Set();
  for (var v of this.values()) newSet.add(f(v));
  return newSet;
};

Set.prototype.reduce = function(f,initial) {
  var result = initial;
  for (var v of this) result = f(result, v);
  return result;
};

Set.prototype.filter = function filter(f) {
  var newSet = new Set();
  for (var v of this) if(f(v)) newSet.add(v);
  return newSet;
};

Set.prototype.every = function every(f) {
  for (var v of this) if (!f(v)) return false;
  return true;
};

Set.prototype.some = function some(f) {
  for (var v of this) if (f(v)) return true;
  return false;
};

Hãy lấy một bộ nhỏ

let s = new Set([1,2,3,4]);

Và một vài chức năng nhỏ ngu ngốc

const times10 = x => x * 10;
const add = (x,y) => x + y;
const even = x => x % 2 === 0;

Và xem chúng hoạt động như thế nào

s.map(times10);    //=> Set {10,20,30,40}
s.reduce(add, 0);  //=> 10
s.filter(even);    //=> Set {2,4}
s.every(even);     //=> false
s.some(even);      //=> true

Chẳng phải điều đó thật tuyệt sao? Vâng, tôi cũng nghĩ vậy. So sánh điều đó với việc sử dụng iterator xấu xí

// puke
let newSet = new Set();
for (let v in s) {
  newSet.add(times10(v));
}

Và

// barf
let sum = 0;
for (let v in s) {
  sum = sum + v;
}

Có cách nào tốt hơn để thực hiện map và reduce bằng cách sử dụng Set trong JavaScript không?

Câu trả lời #1

Một cách viết tắt để làm điều đó là chuyển nó thành mảng thông qua toán tử spread của ES6.

Sau đó tất cả các hàm mảng đều có sẵn cho bạn.

const mySet = new Set([1,2,3,4]);
[...mySet].reduce(...);

Câu trả lời #2

Tóm tắt cuộc thảo luận từ các bình luận: mặc dù không có lý do kỹ thuật nào để đặt không có reduce, nhưng hiện tại nó không được cung cấp và chúng ta chỉ có thể hy vọng điều đó sẽ thay đổi trong ES7.

Đối với map, chỉ gọi nó một mình có thể vi phạm ràng buộc Set, vì vậy sự xuất hiện của nó ở đây có thể gây tranh cãi.

Hãy xem xét việc ánh xạ với một hàm (a) => 42 - nó sẽ thay đổi kích thước của tập hợp thành 1, và điều này có thể hoặc không phải là những gì bạn muốn.

Nếu bạn đồng ý với việc vi phạm điều đó vì ví dụ như bạn sắp gấp bài bất cứ cách nào, bạn có thể áp dụng phần map trên từng phần tử ngay trước khi truyền chúng vào reduce, do đó chấp nhận rằng tập hợp trung gian (mà lúc này không phải là Set) sẽ được giảm có thể có các phần tử trùng lặp. Điều này về cơ bản tương đương với việc chuyển sang Array để xử lý.

Câu trả lời #3

Nguyên nhân thiếu map/reduce/filter trên các tập hợp Map/Set dường như chủ yếu là do mối quan tâm về khái niệm. Liệu mỗi loại tập hợp trong Javascript thực sự có nên chỉ định các phương thức lặp riêng của nó chỉ để cho phép điều này không

const mySet = new Set([1,2,3]);
const myMap = new Map([[1,1],[2,2],[3,3]]);

mySet.map(x => x + 1);
myMap.map(([k, x]) => [k, x + 1]);

thay vì

new Set(Array.from(mySet.values(), x => x + 1));
new Map(Array.from(myMap.entries(), ([k, x]) => [k, x + 1]));

Một phương án thay thế là chỉ định map/reduce/filter như một phần của giao thức iterable/iterator, vì entries/values/keys trả về Iterator. Tuy nhiên, có thể tưởng tượng rằng không phải mọi iterable cũng đều có thể "mappable". Một phương án thay thế khác là chỉ định một "giao thức collection" riêng cho mục đích này.

Tuy nhiên, tôi không biết về cuộc thảo luận hiện tại về chủ đề này tại ES.

Bài viết liên quan