แปลงอาเรย์วัตถุเป็นแผนที่แฮชซึ่งจัดทำดัชนีโดยค่าแอตทริบิวต์ของวัตถุ


305

ใช้ Case

กรณีการใช้งานคือการแปลงอาร์เรย์ของวัตถุให้เป็นแผนที่แฮชตามสตริงหรือฟังก์ชั่นที่มีให้เพื่อประเมินและใช้เป็นกุญแจสำคัญในแผนที่แฮชและค่าเป็นวัตถุเอง กรณีทั่วไปของการใช้สิ่งนี้กำลังแปลงอาเรย์ของวัตถุเป็นแผนที่แฮชของวัตถุ

รหัส

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

function isFunction(func) {
    return Object.prototype.toString.call(func) === '[object Function]';
}

/**
 * This function converts an array to hash map
 * @param {String | function} key describes the key to be evaluated in each object to use as key for hashmap
 * @returns Object
 * @Example 
 *      [{id:123, name:'naveen'}, {id:345, name:"kumar"}].toHashMap("id")
 *      Returns :- Object {123: Object, 345: Object}
 *
 *      [{id:123, name:'naveen'}, {id:345, name:"kumar"}].toHashMap(function(obj){return obj.id+1})
 *      Returns :- Object {124: Object, 346: Object}
 */
Array.prototype.toHashMap = function(key) {
    var _hashMap = {}, getKey = isFunction(key)?key: function(_obj){return _obj[key];};
    this.forEach(function (obj){
        _hashMap[getKey(obj)] = obj;
    });
    return _hashMap;
};

คุณสามารถหาเค้าที่นี่: อาร์เรย์แปลงของวัตถุที่จะ HashMap


คุณสามารถใช้ JavaScript Map แทน Object ตรวจสอบstackoverflow.com/a/54246603/5042169
Jun711

คำตอบ:


471

นี่เป็นเรื่องเล็กน้อยที่เกี่ยวข้องกับArray.prototype.reduce:

var arr = [
    { key: 'foo', val: 'bar' },
    { key: 'hello', val: 'world' }
];

var result = arr.reduce(function(map, obj) {
    map[obj.key] = obj.val;
    return map;
}, {});

console.log(result);
// { foo:'bar', hello:'world' }

หมายเหตุ: Array.prototype.reduce()คือ IE9 + ดังนั้นหากคุณต้องการสนับสนุนเบราว์เซอร์รุ่นเก่าคุณจะต้องทำการเติมข้อมูล


47
result = arr.reduce((map, obj) => (map[obj.key] = obj.val, map), {});สำหรับแฟน ๆ ซับใน ES6: D
Teodor Sandu

31
@Mtz สำหรับแฟน ๆ ES6 หนึ่งบรรทัดตอบสนอง mateuscb result = new Map(arr.map(obj => [obj.key, obj.val]));ด้านล่างมีขนาดเล็กมากและทำความสะอาด: สิ่งสำคัญที่สุดคือทำให้ชัดเจนว่าแผนที่จะถูกส่งกลับ
Ryan Shillington

2
@ RyanShillington เราอยู่ในบริบทของคำตอบที่นี่ซึ่งArray.prototype.reduceเสนอโดย jmar777 Mapสั้นกว่าจริง แต่มันก็เป็นสิ่งที่แตกต่าง ฉันทำตามความตั้งใจเดิม จำไว้ว่านี่ไม่ใช่ฟอรัมคุณอาจต้องการอ่านเพิ่มเติมเกี่ยวกับโครงสร้าง SO Q / A
Teodor Sandu

2
@Mtz ยุติธรรมเพียงพอ
Ryan Shillington

1
นี่ไม่ใช่สิ่งที่ขอ IMHO { "foo": {key: 'foo', val: 'bar'}, "hello": {key: 'hello', val: 'world'} }ผลที่ถูกต้องสำหรับอาร์เรย์ที่แสดงจะเป็น: โปรดทราบว่าแต่ละองค์ประกอบเดิมควรจะเก็บไว้อย่างครบถ้วน หรือการใช้ข้อมูลของ Q {"345": {id:345, name:"kumar"}, ...}นี้: การแก้ไข: เปลี่ยนรหัสเป็นmap[obj.key] = obj;
ToolmakerSteve

302

ใช้ ES6 Map ( สนับสนุนค่อนข้างดี ) คุณสามารถลอง:

var arr = [
    { key: 'foo', val: 'bar' },
    { key: 'hello', val: 'world' }
];

var result = new Map(arr.map(i => [i.key, i.val]));

// When using TypeScript, need to specify type:
// var result = arr.map((i): [string, string] => [i.key, i.val])

// Unfortunately maps don't stringify well.  This is the contents in array form.
console.log("Result is: " + JSON.stringify([...result])); 
// Map {"foo" => "bar", "hello" => "world"}


4
ยังเป็นสิ่งสำคัญที่จะทราบว่าจะได้รับบางสิ่งบางอย่างออกมาจากMapคุณจำเป็นต้องใช้เมื่อเทียบกับเพียงresult.get(keyName) result[keyName]นอกจากนี้โปรดทราบว่าวัตถุใด ๆ ที่สามารถใช้เป็นกุญแจและไม่ใช่แค่สตริง
Simon_Weaver

5
รุ่นของTypeScriptอื่นจะมีลักษณะดังนี้: var result = new Map(arr.map(i => [i.key, i.val] as [string, string]));ซึ่งบางคนอาจเข้าใจได้ง่ายขึ้น as [string, string]เพิ่มการโยนประเภทโน้ต
AlexV

เมื่อฉันเรียกใช้รหัสนี้ใน Chrome v71 ฉันยังคงได้รับอาร์เรย์:Result is: [["foo","bar"],["hello","world"]]
Jean-François Beauchamp

PS resultไม่ใช่แฮชตามที่ OP ต้องการ
Jean-François Beauchamp

1
รุ่นตัวพิมพ์อื่น:var result = new Map<string, string>(arr.map(i => [i.key, i.val]));
Aziz Javed

39

ด้วยlodashสิ่งนี้สามารถทำได้โดยใช้keyBy :

var arr = [
    { key: 'foo', val: 'bar' },
    { key: 'hello', val: 'world' }
];

var result = _.keyBy(arr, o => o.key);

console.log(result);
// Object {foo: Object, hello: Object}

นั่นไม่ใช่แฮชแมป
Pomme De Terre

37

ใช้การแพร่กระจาย ES6 + Object.assign:

array = [{key: 'a', value: 'b', redundant: 'aaa'}, {key: 'x', value: 'y', redundant: 'zzz'}]

const hash = Object.assign({}, ...array.map(s => ({[s.key]: s.value})));

console.log(hash) // {a: b, x: y}

2
สมบูรณ์แบบเพียงสิ่งที่ฉันต้องการ;)
ปิแอร์

1
const hash = Object.assign({}, ...(<{}>array.map(s => ({[s.key]: s.value}))));ต้องทำการเปลี่ยนแปลงนี้เพื่อทำงานกับ typescript
ruwan800

24

การใช้ตัวดำเนินการสเปรด:

const result = arr.reduce(
    (accumulator, target) => ({ ...accumulator, [target.key]: target.val }),
    {});

การสาธิตของข้อมูลโค้ดบนjsFiddle


7
ฉันตรงนี้เพราะสิ่งนี้! ตัวดำเนินการสเปรดดำเนินการอย่างไรในแบบเก่าปกติเพียงกำหนดคีย์ใหม่และคืนค่าตัวสะสม เนื่องจากเป็นการสร้างสำเนาใหม่ทุกครั้งการแพร่กระจายจะทำงานได้ไม่ดี !
AMTourky

1
ตอนนี้คุณกระจายไปทุกรอบซ้ำ มันควรจะปลอดภัยที่จะกลายพันธุ์ในตัวลด `` `const const = arr.reduce ((ตัวสะสม, เป้าหมาย) => {accumulator [target.key]: target.val; return accumulator}, {}); `` `
MTJ

17

คุณสามารถใช้Array.prototype.reduce ()และที่เกิดขึ้นจริง JavaScript แผนที่แทนเพียง JavaScript วัตถุ

let keyValueObjArray = [
  { key: 'key1', val: 'val1' },
  { key: 'key2', val: 'val2' },
  { key: 'key3', val: 'val3' }
];

let keyValueMap = keyValueObjArray.reduce((mapAccumulator, obj) => {
  // either one of the following syntax works
  // mapAccumulator[obj.key] = obj.val;
  mapAccumulator.set(obj.key, obj.val);

  return mapAccumulator;
}, new Map());

console.log(keyValueMap);
console.log(keyValueMap.size);

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

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

  • กุญแจของวัตถุคือสตริงและสัญลักษณ์ในขณะที่พวกเขาสามารถเป็นค่าใด ๆ สำหรับแผนที่รวมถึงฟังก์ชั่นวัตถุและดั้งเดิมใด ๆ
  • คีย์ในแผนที่จะถูกจัดเรียงในขณะที่ปุ่มที่เพิ่มให้กับวัตถุนั้นไม่ได้จัดทำ ดังนั้นเมื่อวนซ้ำวัตถุแผนที่จะส่งคืนคีย์ตามลำดับการแทรก
  • คุณสามารถรับขนาดของแผนที่ได้อย่างง่ายดายด้วยคุณสมบัติขนาดในขณะที่ต้องกำหนดจำนวนคุณสมบัติในวัตถุด้วยตนเอง
  • แผนที่คือการทำซ้ำและสามารถทำซ้ำได้โดยตรงในขณะที่การวนซ้ำวัตถุต้องได้รับกุญแจในบางแบบและวนซ้ำมัน
  • วัตถุมีต้นแบบดังนั้นจึงมีปุ่มเริ่มต้นในแผนที่ที่สามารถชนกับปุ่มของคุณได้หากคุณไม่ระวัง ในฐานะของ ES5 นี้สามารถข้ามได้โดยใช้ map = Object.create (null) แต่สิ่งนี้ทำได้ยาก
  • แผนที่อาจทำงานได้ดีขึ้นในสถานการณ์ที่เกี่ยวข้องกับการเพิ่มและการลบคู่กุญแจบ่อยครั้ง

1
คุณไม่มีลูกศร เปลี่ยน(mapAccumulator, obj) {...}สำหรับ(mapAccumulator, obj) => {...}
พฤษภาคม

15

คุณสามารถใช้ใหม่ Object.fromEntries()วิธีการ

ตัวอย่าง:

const array = [
   {key: 'a', value: 'b', redundant: 'aaa'},
   {key: 'x', value: 'y', redundant: 'zzz'}
]

const hash = Object.fromEntries(
   array.map(e => [e.key, e.value])
)

console.log(hash) // {a: b, x: y}



4

นี่คือสิ่งที่ฉันทำใน TypeScript ฉันมีห้องสมุดเล็ก ๆ น้อย ๆ ที่ฉันใส่สิ่งนี้

export const arrayToHash = (array: any[], id: string = 'id') => 
         array.reduce((obj, item) =>  (obj[item[id]] = item , obj), {})

การใช้งาน:

const hash = arrayToHash([{id:1,data:'data'},{id:2,data:'data'}])

หรือถ้าคุณมีตัวระบุอื่นที่ไม่ใช่ 'id'

const hash = arrayToHash([{key:1,data:'data'},{key:2,data:'data'}], 'key')

ถ้าคุณต้องการใช้วัตถุเป็นกุญแจคุณจะต้องใช้แผนที่แทนวัตถุเพราะตัวพิมพ์ดีดจะไม่ยอมให้คุณใช้วัตถุเป็นกุญแจ
Dany Dhondt

3

มีวิธีที่ดีกว่าในการทำเช่นนี้ตามที่อธิบายโดยผู้โพสต์อื่น ๆ แต่ถ้าฉันต้องการที่จะยึดมั่นกับวิธีที่บริสุทธิ์ JS และเฒ่าแฟชั่นที่นี่ก็คือ:

var arr = [
    { key: 'foo', val: 'bar' },
    { key: 'hello', val: 'world' },
    { key: 'hello', val: 'universe' }
];

var map = {};
for (var i = 0; i < arr.length; i++) {
    var key = arr[i].key;
    var value = arr[i].val;

    if (key in map) {
        map[key].push(value);
    } else {
        map[key] = [value];
    }
}

console.log(map);

จะแนะนำให้ใช้วิธีลดขนาดเล็กกว่าวิธีนี้หรือไม่ ฉันรู้สึกอยากใช้วิธีนี้ มันง่ายและง่ายต่อการดูทุกสิ่ง
Santhosh Yedidi

ฉันรักวิธีการนี้ ฉันคิดว่าบางครั้งรหัสที่ง่ายที่สุดดีที่สุด ทุกวันนี้ผู้คนถูกปิดใช้งานโดยความไม่แน่นอน แต่ตราบใดที่มันยังมีอยู่ความไม่แน่นอนก็น่ากลัวและเป็นนักแสดง
Luis Aceituno

3

หากคุณต้องการแปลงเป็น ES6 Mapใหม่ให้ทำดังนี้

var kvArray = [['key1', 'value1'], ['key2', 'value2']];
var myMap = new Map(kvArray);

ทำไมคุณควรใช้แผนที่ประเภทนี้? นั่นขึ้นอยู่กับคุณ ลองดูที่นี้


2

ใช้ Javascript ง่าย ๆ

var createMapFromList = function(objectList, property) {
    var objMap = {};
    objectList.forEach(function(obj) {
      objMap[obj[property]] = obj;
    });
    return objMap;
  };
// objectList - the array  ;  property - property as the key

3
ไม่ได้ใช้. map (... ) อย่างไม่มีจุดหมายในตัวอย่างนี้เนื่องจากคุณไม่ส่งคืนสิ่งใดในนั้น ฉันขอแนะนำสำหรับแต่ละคนในกรณีนี้
cuddlecheek

2

ด้วยlodash:

const items = [
    { key: 'foo', value: 'bar' },
    { key: 'hello', value: 'world' }
];

const map = _.fromPairs(items.map(item => [item.key, item.value]));

console.log(map); // { foo: 'bar', hello: 'world' }

1

การปรับปรุงเล็กน้อยในการreduceใช้งาน:

var arr = [
    { key: 'foo', val: 'bar' },
    { key: 'hello', val: 'world' }
];

var result = arr.reduce((map, obj) => ({
    ...map,
    [obj.key] = obj.val
}), {});

console.log(result);
// { foo: 'bar', hello: 'world' }

มันเร็วกว่าคำตอบอื่น ๆ ?
orad

@orad อาจไม่ได้เนื่องจากมันกระจายการสะสมและสร้างวัตถุใหม่แต่ละการวนซ้ำ
Luckylooke

1

ลอง

let toHashMap = (a,f) => a.reduce((a,c)=> (a[f(c)]=c,a),{});


0

ต่อไปนี้เป็นตัวอย่างเล็ก ๆ ที่ฉันสร้างขึ้นในจาวาสคริปต์เพื่อแปลงอาร์เรย์ของวัตถุเป็นแผนที่แฮชดัชนีโดยค่าแอตทริบิวต์ของวัตถุ คุณสามารถจัดเตรียมฟังก์ชั่นเพื่อประเมินคีย์ของแฮชแม็พ (เวลารัน)

function isFunction(func){
    return Object.prototype.toString.call(func) === '[object Function]';
}

/**
 * This function converts an array to hash map
 * @param {String | function} key describes the key to be evaluated in each object to use as key for hasmap
 * @returns Object
 * @Example 
 *      [{id:123, name:'naveen'}, {id:345, name:"kumar"}].toHashMap("id")
        Returns :- Object {123: Object, 345: Object}

        [{id:123, name:'naveen'}, {id:345, name:"kumar"}].toHashMap(function(obj){return obj.id+1})
        Returns :- Object {124: Object, 346: Object}
 */
Array.prototype.toHashMap = function(key){
    var _hashMap = {}, getKey = isFunction(key)?key: function(_obj){return _obj[key];};
    this.forEach(function (obj){
        _hashMap[getKey(obj)] = obj;
    });
    return _hashMap;
};

คุณสามารถค้นหาส่วนสำคัญได้ที่นี่: https://gist.github.com/naveen-ithappu/c7cd5026f6002131c1fa


11
ได้โปรดได้โปรดอย่าแนะนำให้ขยายArray.prototype!
jmar777

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