Làm cách nào để ánh xạ / thu nhỏ / lọc một Set trong JavaScript?


131

Có cách nào để map/ reduce/ filter/ etc a Settrong JavaScript hay tôi sẽ phải tự viết?

Đây là một số Set.prototypephần mở rộng 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 tập một chút

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

Và một số 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 cách họ làm việc

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

Điều đó không tốt sao? Vâng, tôi cũng nghĩ vậy. So sánh điều đó với cách sử dụng lặp xấu xí

// puke
let newSet = new Set();
for (let v in s) {
  newSet.add(times10(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 mapreducesử dụng SetJavaScript không?


Vấn đề với việc giảm bản đồ Setlà Bộ không phải là Functor.
Bartek Banachewicz

@BartekBanachewicz vâng đó là một vấn đề ... phải không?
Cảm ơn bạn

2
Vâng, xem xét var s = new Set([1,2,3,4]); s.map((a) => 42);. Nó thay đổi số lượng phần tử, mapthường không được phép làm. Thậm chí tệ hơn nếu bạn chỉ so sánh các bộ phận của các vật thể được giữ, bởi vì về mặt kỹ thuật, nó không xác định được cái nào bạn sẽ nhận được.
Bartek Banachewicz

Tôi đã xem xét điều đó, nhưng tôi không chắc rằng tôi (cá nhân) sẽ coi điều đó không hợp lệ. OK, ít nhất là forEachtồn tại cho kịch bản đó, nhưng tại sao không reduce?
Cảm ơn bạn

Câu trả lời:


105

Một cách ngắn gọn để làm điều đó là chuyển đổi nó thành một mảng thông qua toán tử trải ES6.

Sau đó, tất cả các chức năng mảng có sẵn cho bạn.

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

Bởi vì các chức năng không có sẵn cho Set! Đây là một cách giải quyết đầy đủ, được hướng dẫn và hiểu chưa được trình bày trong chủ đề này. Thực tế là "mất nhiều thời gian hơn" là một cái giá đáng buồn phải trả cho một cách giải quyết cho đến khi Set thực hiện các tính năng này!
ZephDavies

1
Sự khác biệt giữa cái này và Array.from
pete

9
Đối với tôi ít nhất, sự khác biệt giữa cái này và Array.fromcái đó Array.fromhoạt động với TypeScript. Sử dụng [...mySet]cho lỗi:TS2461: Type 'Set<number>' is not an array type.
Mikal Madsen

1
Đối với lây lan so với Array.from (), xem stackoverflow.com/a/40549565/5516454 Về cơ bản, cả hai đều có thể sử dụng ở đây. Array.from () cũng có thể thực hiện các đối tượng giống như mảng không thực hiện @@iteratorphương thức.
ZephDavies

vẫn không làm việc cho tôi với bản thảo. Tôi nhận đượcERROR TypeError: this.sausages.slice is not a function
Simon_Weaver

22

Để tổng hợp các 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 được đặt thành khôngreduce, nhưng hiện tại nó không được cung cấp và chúng tôi chỉ có thể hy vọng nó thay đổi trong ES7.

Đối với việc mapgọi nó một mình có thể vi phạm các Setràng buộc, vì vậy sự hiện diện của nó ở đây có thể gây tranh cãi.

Xem xét á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 thể là điều bạn muốn.

Nếu bạn đồng ý với việc vi phạm điều đó bởi vì ví dụ như bạn sẽ gấp lại, bạn có thể áp dụng mapphần đó cho mọi phần tử ngay trước khi chuyển chúng sang reduce, do đó chấp nhận rằng bộ sưu tập trung gian ( không phải là Bộ tại thời điểm này ) sẽ được giảm có thể có các yếu tố trùng lặp. Điều này về cơ bản tương đương với việc chuyển đổi sang Array để xử lý.


1
Điều này chủ yếu là tốt, ngoại trừ (sử dụng mã ở trên), s.map(a => 42)sẽ dẫn đến kết quả Set { 42 }được ánh xạ sẽ có độ dài khác nhau nhưng sẽ không có các yếu tố "trùng lặp". Có thể cập nhật từ ngữ và tôi sẽ chấp nhận câu trả lời này.
Cảm ơn bạn

@naomik Oh derp Tôi vừa hoàn thành ly cà phê đầu tiên của mình khi viết nó. Ở cái nhìn thứ hai, bộ sưu tập trung gian được thông qua để giảm có thể có các yếu tố ngay lập tức nếu bạn chấp nhận nó không phải là một bộ - đó là ý tôi.
Bartek Banachewicz

Ồ tôi hiểu rồi - bản đồ phải ánh xạ cùng loại, do đó có thể xảy ra va chạm trong bộ đích. Khi tôi tìm thấy câu hỏi này, tôi đã nghĩ rằng bản đồ sẽ ánh xạ tới một mảng từ một tập hợp. (như thể bạn đã thiết lập.toArray (). map () `
Simon_Weaver

2
Trong Scala và Haskell, các bộ hỗ trợ thao tác bản đồ - nó có thể giảm số lượng phần tử trong bộ.
Velizar Hristov

8

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

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 cách khác là chỉ định ánh xạ / thu nhỏ / bộ lọc như là một phần của giao thức iterable / iterator, vì entries/ values/ keysreturn Iterators. Có thể hiểu được mặc dù không phải mọi lần lặp cũng là "có thể lập bản đồ". Một cách khác là chỉ định một "giao thức thu thập" riêng cho mục đích này.

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

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.