จะแมป / ลด / กรองชุดใน JavaScript ได้อย่างไร?


135

มีวิธีใดบ้างที่จะmap/ reduce/ filter/ etc a Setใน JavaScript หรือฉันจะต้องเขียนเอง?

นี่คือส่วนSet.prototypeขยายที่เหมาะสม

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

มาเซ็ตกันหน่อย

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

และฟังก์ชั่นเล็ก ๆ น้อย ๆ โง่ ๆ

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

และดูว่าพวกเขาทำงานอย่างไร

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

ไม่ดีเหรอ? ใช่ฉันก็คิดเช่นนั้นเหมือนกัน เปรียบเทียบกับการใช้ตัววนซ้ำที่น่าเกลียด

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

มีวิธีใดที่ดีกว่าในการบรรลุmapและreduceใช้งานSetใน JavaScript?


ปัญหาเกี่ยวกับการลดแผนที่ a Setคือชุดไม่ใช่ Functors
Bartek Banachewicz

@BartekBanachewicz ใช่นั่นเป็นปัญหา ... ใช่มั้ย?
ขอบคุณ

2
ลองพิจารณาvar s = new Set([1,2,3,4]); s.map((a) => 42);ดู มันเปลี่ยนจำนวนองค์ประกอบซึ่งmapโดยทั่วไปไม่ควรทำ ยิ่งไปกว่านั้นถ้าคุณเปรียบเทียบเฉพาะบางส่วนของวัตถุที่เก็บไว้เพราะในทางเทคนิคแล้วมันไม่ได้ระบุว่าคุณจะได้รับสิ่งใด
Bartek Banachewicz

ฉันได้พิจารณาแล้ว แต่ฉันไม่แน่ใจว่าฉัน (โดยส่วนตัว) จะพิจารณาว่าไม่ถูกต้อง ตกลงอย่างน้อยก็forEachมีอยู่สำหรับสถานการณ์นั้น แต่ทำไมถึงไม่มีreduceล่ะ?
ขอบคุณ

4
การอ่านที่เกี่ยวข้อง: esdiscuss.org/topic/set-some-every-reduce-filter-map-methods
CodingIntrigue

คำตอบ:


107

วิธีง่ายๆในการทำคือการแปลงเป็นอาร์เรย์ผ่านตัวดำเนินการสเปรด ES6

จากนั้นฟังก์ชันอาร์เรย์ทั้งหมดจะพร้อมใช้งานสำหรับคุณ

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

1
เนื่องจากฟังก์ชั่นใช้งานไม่ได้สำหรับ Set! นี่เป็นวิธีแก้ปัญหาที่สมบูรณ์แนะนำและเข้าใจซึ่งยังไม่ปรากฏในหัวข้อนี้ ความจริงที่ว่า 'ใช้เวลานานกว่า' เป็นราคาที่น่าเศร้าที่ต้องจ่ายสำหรับการแก้ปัญหาชั่วคราวจนกว่า Set จะใช้คุณสมบัติเหล่านี้!
ZephDavies

1
อะไรคือความแตกต่างระหว่างสิ่งนี้กับ Array.from
pete

10
อย่างน้อยสำหรับฉันความแตกต่างระหว่างสิ่งนี้กับArray.fromที่ใช้Array.fromงานได้กับ TypeScript การใช้[...mySet]ทำให้เกิดข้อผิดพลาด:TS2461: Type 'Set<number>' is not an array type.
Mikal Madsen

1
สำหรับการแพร่กระจายเทียบกับ Array.from () โปรดดูstackoverflow.com/a/40549565/5516454 โดยพื้นฐานแล้วทั้งสองอย่างสามารถใช้ได้ที่นี่ Array.from () สามารถทำอ็อบเจ็กต์ที่คล้ายอาร์เรย์เพิ่มเติมซึ่งไม่ได้ใช้@@iteratorเมธอด
ZephDavies

ยังใช้ไม่ได้กับฉันกับ typescript ฉันจะได้รับERROR TypeError: this.sausages.slice is not a function
Simon_Weaver

22

เพื่อสรุปผลการอภิปรายจากความคิดเห็น: ในขณะที่ไม่มีเหตุผลทางเทคนิคสำหรับการตั้งค่าที่จะไม่ได้reduceก็ไม่ได้ให้ในขณะนี้และเราสามารถหวังว่ามันจะมีการเปลี่ยนแปลงใน ES7

สำหรับการmapเรียกมันอย่างเดียวอาจละเมิดSetข้อ จำกัด ดังนั้นการปรากฏตัวที่นี่อาจเป็นที่ถกเถียงกัน

พิจารณาการแมปด้วยฟังก์ชัน(a) => 42ซึ่งจะเปลี่ยนขนาดของชุดเป็น 1 และอาจเป็นหรือไม่ใช่สิ่งที่คุณต้องการ

หากคุณตกลงที่จะละเมิดสิ่งนั้นเพราะเช่นคุณกำลังจะพับอยู่แล้วคุณสามารถใช้mapส่วนนั้นกับทุกองค์ประกอบก่อนที่จะส่งต่อreduceดังนั้นจึงยอมรับว่าคอลเลกชันระดับกลาง ( ซึ่งไม่ใช่ Set ณ จุดนี้ ) การลดลงอาจมีองค์ประกอบที่ซ้ำกัน สิ่งนี้เทียบเท่ากับการแปลงเป็น Array เพื่อทำการประมวลผล


1
ซึ่งส่วนใหญ่เป็นสิ่งที่ดียกเว้น (การใช้โค้ดด้านบน) s.map(a => 42)จะทำให้ผลลัพธ์Set { 42 }ที่แมปมีความยาวต่างกัน แต่จะไม่มีองค์ประกอบ "ซ้ำกัน" อาจจะอัปเดตข้อความและฉันจะยอมรับคำตอบนี้
ขอบคุณ

@naomik Oh derp ฉันเพิ่งกินกาแฟแก้วแรกเสร็จเมื่อเขียนว่า ในรูปลักษณ์ที่สองคอลเลกชันระดับกลางที่ส่งผ่านเพื่อลดอาจมีองค์ประกอบทันทีหากคุณยอมรับว่าไม่ใช่ชุด - นั่นคือฉันหมายถึง
Bartek Banachewicz

โอ้เข้าใจแล้ว - แผนที่ต้องแมปเป็นประเภทเดียวกันดังนั้นอาจเกิดการชนกันในปลายทางได้ เมื่อฉันพบคำถามนี้ฉันคิดว่าแผนที่จะแมปกับอาร์เรย์จากชุด . (เช่นถ้าคุณทำ set.toArray () แผนที่ () `
Simon_Weaver

2
ใน Scala และ Haskell ชุดรองรับการทำงานของแผนที่ซึ่งสามารถลดจำนวนองค์ประกอบในชุดได้
Velizar Hristov

8

สาเหตุของการขาดmap/ reduce/ filterบนMap/ Setคอลเลกชันดูเหมือนจะเป็นปัญหาด้านแนวคิดเป็นหลัก หากคอลเลกชันแต่ละประเภทใน Javascript ระบุวิธีการทำซ้ำของตัวเองเท่านั้นเพื่ออนุญาตสิ่งนี้

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

แทน

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

อีกทางเลือกหนึ่งคือระบุ map / ลด / กรองเป็นส่วนหนึ่งของโปรโตคอล iterable / iterator เนื่องจากentries/ values/ keysreturn Iterators เป็นไปได้แม้ว่าจะไม่สามารถทำซ้ำได้ทุกครั้งที่ "แมป" อีกทางเลือกหนึ่งคือการระบุ "โปรโตคอลการรวบรวม" แยกต่างหากเพื่อจุดประสงค์นี้

อย่างไรก็ตามฉันไม่ทราบการสนทนาปัจจุบันในหัวข้อนี้ที่ ES

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.