จะแปลงวัตถุธรรมดาเป็นแผนที่ ES6 ได้อย่างไร?


128

ด้วยเหตุผลบางอย่างฉันไม่พบสิ่งง่ายๆนี้ในเอกสาร MDN (บางทีฉันอาจจะพลาด)

ฉันคาดว่าจะได้ผล:

const map = new Map({foo: 'bar'});

map.get('foo'); // 'bar'

... แต่บรรทัดแรกพ่น TypeError: (var)[Symbol.iterator] is not a function

ฉันจะสร้างแผนที่จากวัตถุธรรมดาได้อย่างไร ก่อนอื่นฉันต้องแปลงเป็นอาร์เรย์อาร์เรย์ของคู่คีย์ - ค่าหรือไม่


2
FWIW มันอาจจะมีมูลค่าการเปลี่ยนคำตอบที่ได้รับการยอมรับของคุณจากเหมืองเพื่อNils'หรือbergi ของ Object.entriesเป็นแนวทางที่ดีกว่าจริงๆObject.keysและวิธีการทำงานของเครื่องกำเนิดไฟฟ้าของ bergi นั้นตรงกว่าObject.keysหรือObject.entries.
TJ Crowder

คำตอบ:


195

ใช่ตัวMapสร้างรับอาร์เรย์ของคู่คีย์ - ค่า

Object.entriesเป็นวัตถุวิธีการใหม่คงมีอยู่ในES2017 (19.1.2.5)

const map = new Map(Object.entries({foo: 'bar'}));

map.get('foo'); // 'bar'

ปัจจุบันมีการใช้งานใน Firefox 46+ และ Edge 14+ และ Chrome เวอร์ชันใหม่กว่า

หากคุณต้องการรองรับสภาพแวดล้อมที่เก่ากว่าและการถ่ายโอนข้อมูลไม่ใช่ตัวเลือกสำหรับคุณให้ใช้โพลีฟิลล์เช่นที่แนะนำโดย georg:

Object.entries = typeof Object.entries === 'function' ? Object.entries : obj => Object.keys(obj).map(k => [k, obj[k]]);

3
"โพลีฟิลล์" จะค่อนข้างไม่สำคัญ:Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]])
จอร์เจีย

4
Object.entriesลงจอดใน Node 7.x ที่เหมาะสม (ไม่มีธง) btw
Lee Benson

2
ไม่เพียง แต่ Object.entries ใน Node7 เท่านั้น แต่ยังเป็นส่วนหนึ่งของข้อกำหนดขั้นสุดท้ายของ ES2017 ดังนั้นนี่ควรเป็นคำตอบที่ยอมรับ IMO
AnilRedshift

1
@AnilRedshift - นี่หรือคำตอบของฟังก์ชันเครื่องกำเนิดไฟฟ้าเนื่องจาก OP ขอวิธีแก้ปัญหาโดยหลีกเลี่ยงตัวกลาง
TJ Crowder

2
น่าเสียดายที่มันจะไม่
Nemanja Milosavljevic

78

โปรดดูคำตอบนิลส์ใช้Object.entriesและ / หรือคำตอบ bergi โดยใช้ฟังก์ชั่นเครื่องกำเนิดไฟฟ้า แม้ว่าจะObject.entriesยังไม่อยู่ในข้อกำหนดเมื่อถามคำถาม แต่ก็อยู่ในขั้นตอนที่ 4ดังนั้นจึงปลอดภัยที่จะเติม polyfill และใช้งานได้ในเดือนเมษายน 2559 (เพียง) (เพิ่มเติมเกี่ยวกับขั้นตอนที่นี่ ) และฟังก์ชันเครื่องกำเนิดไฟฟ้าอยู่ใน ES2015 สหกรณ์ถามเฉพาะเพื่อหลีกเลี่ยงตัวกลางและในขณะที่เครื่องกำเนิดไฟฟ้าไม่สมบูรณ์หลีกเลี่ยงที่จะได้งานที่ดีกว่าด้านล่างหรือ Object.enties(เล็กน้อย)

FWIW โดยใช้Object.entries:

  • สร้างอาร์เรย์[name, value]อาร์เรย์ที่จะส่งผ่านไปnew Map
  • ตัวMapสร้างเรียกใช้ฟังก์ชันบนอาร์เรย์เพื่อรับตัวทำซ้ำ อาร์เรย์สร้างและส่งคืนอ็อบเจ็กต์จำนวนเต็มอาร์เรย์
  • ตัวMapสร้างใช้ออบเจ็กต์ตัววนซ้ำเพื่อรับรายการ ( [name, value]อาร์เรย์) และสร้างแผนที่

การใช้เครื่องกำเนิดไฟฟ้า:

  • สร้างวัตถุเครื่องกำเนิดไฟฟ้าอันเป็นผลมาจากการเรียกฟังก์ชันเครื่องกำเนิดไฟฟ้า
  • คอนMapสตรัคเตอร์เรียกใช้ฟังก์ชันบนอ็อบเจ็กต์ตัวสร้างเพื่อรับตัวทำซ้ำจากมัน วัตถุกำเนิดจะส่งคืนตัวเอง
  • ตัวMapสร้างใช้วัตถุตัวสร้าง (เป็นตัววนซ้ำ) เพื่อรับรายการ ( [name, value]อาร์เรย์) และสร้างแผนที่

ดังนั้น: ตัวกลางน้อยกว่าหนึ่งตัว (อาร์เรย์จากObject.entries)

อย่างไรก็ตามการใช้งานObject.entriesนั้นง่ายกว่าและการสร้างอาร์เรย์นั้นไม่ใช่ปัญหา 99.999% ของเวลา อย่างใดอย่างหนึ่ง แต่ทั้งคู่ดีกว่าด้านล่าง :-)


คำตอบเดิม:

ในการเริ่มต้น a Mapคุณสามารถใช้ตัววนซ้ำใด ๆ ที่ส่งคืนคู่คีย์ / ค่าเป็นอาร์เรย์เช่นอาร์เรย์อาร์เรย์:

const map = new Map([
    ['foo', 'bar']
]);

ไม่มีการแปลงในตัวจากวัตถุเป็นแผนที่ แต่ทำได้อย่างง่ายดายด้วยObject.keys:

const map = new Map();
let obj = {foo: 'bar'};
Object.keys(obj).forEach(key => {
    map.set(key, obj[key]);
});

แน่นอนคุณสามารถให้หน้าที่คนงานจัดการกับตัวเองได้:

function buildMap(obj) {
    let map = new Map();
    Object.keys(obj).forEach(key => {
        map.set(key, obj[key]);
    });
    return map;
}

แล้วก็

const map = buildMap({foo: 'bar'});

หรือนี่คือเวอร์ชัน l33t ที่ดูดีกว่า (นั่นยังเป็นของอยู่หรือเปล่า):

function buildMap(obj) {
    return Object.keys(obj).reduce((map, key) => map.set(key, obj[key]), new Map());
}

(ใช่Map#setผลตอบแทนอ้างอิงแผนที่. บางคนจะเถียงนี้เป็นabusageของreduce.)

หรือเราสามารถจริงๆไปมากกว่าที่บนความสับสน:

const buildMap = o => Object.keys(o).reduce((m, k) => m.set(k, o[k]), new Map());

ไม่ฉันจะไม่ทำอย่างนั้นจริง :-)


1
ฟิวชั่นของ @ Ohar และ @ การแก้ปัญหาของ var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));TJCrowder:
7vujy0f0hy

17
ดูnew Map(Object.entries(object)) stackoverflow.com/a/36644558/798133
Rafael Xavier

คำตอบ Object.entries ควรเป็นที่ต้องการ
Kir

@Kir - นั่นหรือเครื่องกำเนิดไฟฟ้าใช่ ดูการแก้ไขของฉัน
TJ Crowder

31

ก่อนอื่นฉันต้องแปลงเป็นอาร์เรย์อาร์เรย์ของคู่คีย์ - ค่าหรือไม่

ไม่ตัววนซ้ำของอาร์เรย์คู่คีย์ - ค่าก็เพียงพอแล้ว คุณสามารถใช้สิ่งต่อไปนี้เพื่อหลีกเลี่ยงการสร้างอาร์เรย์กลาง:

function* entries(obj) {
    for (let key in obj)
        yield [key, obj[key]];
}

const map = new Map(entries({foo: 'bar'}));
map.get('foo'); // 'bar'

ตัวอย่างที่ดี - เพียงแค่บันทึกถึงผู้อื่นคุณอาจต้องการทำif(!obj.hasOwnProperties(key)) continue;ทันทีหลังจากเงื่อนไข for loop เพื่อให้แน่ใจว่าคุณไม่ได้รับคุณสมบัติที่สืบทอดมาจากต้นแบบวัตถุ (เว้นแต่คุณจะเชื่อถือวัตถุ แต่ควรทำเช่นนี้ต่อไปเมื่อทำซ้ำวัตถุโดยใช้inเป็นนิสัยที่ดี)
puiu

2
@Puiu ไม่คุณไม่ควรและมันเป็นนิสัยที่ไม่ดี หากคุณไม่ไว้วางใจวัตถุนั้นคุณต้องไม่ไว้วางใจ.hasOwnPropertyทรัพย์สินของมันด้วยและคุณต้องใช้Object.prototype.hasOwnProperty.call(obj, key)
Bergi

2
ใช่ฉันคิดว่าการไม่รบกวนเป็นแนวทางปฏิบัติที่ดีที่สุด คุณควรเชื่อถืออ็อบเจ็กต์ทั้งหมดที่ควรค่าแก่การแจกแจง (เช่นคีย์ - ค่า - แมป) เพื่อไม่มีคุณสมบัติที่สืบทอดมานับได้ (และแน่นอนหลีกเลี่ยงการแจกแจงอาร์เรย์ ) callหากคุณมีกรณีที่หายากของแจงอย่างอื่นและคุณรำคาญคุณอย่างน้อยควรทำอย่างถูกต้องด้วย การประชุมทั้งหมดที่แนะนำobj.hasOwnProperties(key)ดูเหมือนจะไม่รู้ว่ากำลังทำอะไรอยู่
Bergi

3
การแปลงเป็นObjecta Mapเป็นการดำเนินการที่มีราคาแพงและ OP ขอวิธีแก้ปัญหาโดยไม่ใช้ตัวกลางโดยเฉพาะ เหตุใดจึงไม่เป็นคำตอบที่ยกเว้น? การถามแบบนี้คงไร้ประโยชน์มันทำให้ฉันรำคาญ

1
@tmvnty function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}คุณอาจจำเป็นต้องสะกดออกชนิด นอกจากนี้ยังyield [key, obj[key]] as const;ช่วยให้ TypeScript ตระหนักว่ากำลังให้ผลทูเปิลไม่ใช่อาร์เรย์
Bergi

11

คำตอบโดย Nilsอธิบายถึงวิธีการแปลงวัตถุเป็นแผนที่ซึ่งฉันพบว่ามีประโยชน์มาก อย่างไรก็ตาม OP ยังสงสัยว่าข้อมูลนี้อยู่ที่ใดในเอกสาร MDN แม้ว่าจะไม่เคยมีมาก่อนเมื่อถามคำถาม แต่ตอนนี้อยู่ในหน้า MDN สำหรับObject.entries ()ภายใต้หัวข้อการแปลงวัตถุเป็นแผนที่ซึ่งระบุว่า:

การแปลงวัตถุเป็นแผนที่

ตัวnew Map()สร้างยอมรับการทำซ้ำของentries. ด้วยObject.entriesคุณสามารถแปลงจากObjectเป็นMap:

const obj = { foo: 'bar', baz: 42 }; 
const map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }

7
const myMap = new Map(
    Object
        .keys(myObj)
        .map(
            key => [key, myObj[key]]
        )
)

1
ฟิวชั่นของ @ Ohar และ @ การแก้ปัญหาของ var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));TJCrowder:
7vujy0f0hy

ขอบคุณเพราะฉันต้องเรียงคีย์ออบเจ็กต์ด้วย!
scottwernervt


1

ES6

แปลงวัตถุเป็นแผนที่:

const objToMap = (o) => new Map(Object.entries(o));

แปลงแผนที่เป็นวัตถุ:

const mapToObj = (m) => [...m].reduce( (o,v)=>{ o[v[0]] = v[1]; return o; },{} )

หมายเหตุ: ฟังก์ชัน mapToObj ถือว่าคีย์แผนที่เป็นสตริง (จะล้มเหลวเป็นอย่างอื่น)


-1

ด้วยความช่วยเหลือของ JQuery

const myMap = new Map();
$.each( obj, function( key, value ) {
myMap[key] = value;
});

4
ในปี 2019 ฉันรู้สึกว่าเราไม่ควรมองหา jquery เพื่อแก้ปัญหา
illcrx

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