将对象数组转换为哈希图,由对象的属性值索引


305

用例

用例是根据提供的字符串或函数将对象数组转换为哈希图,以评估和用作哈希图中的键,并将值用作对象本身。使用此方法的常见情况是将对象数组转换为对象的哈希图。

以下是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

Answers:


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
Teodor Sandu

31
@Mtz对于ES6单行风扇,下面mateuscb的反应是更小,更清洁: result = new Map(arr.map(obj => [obj.key, obj.val]));。最重要的是,它非常清楚返回了地图。
Ryan Shillington

2
@RyanShillington我们处于答案的上下文中,这是Array.prototype.reducejmar777提出的。Map确实较短,但这是另一回事。我符合最初的意图。请记住,这不是一个论坛,您可能想阅读有关SO Q / A结构的更多信息。
Teodor Sandu

2
@Mtz公平。
Ryan Shillington

1
恕我直言,这不是要求的。显示的数组的正确结果将是:{ "foo": {key: 'foo', val: 'bar'}, "hello": {key: 'hello', val: 'world'} }。请注意,每个原始元素都应完整保留。或使用Q的数据:{"345": {id:345, name:"kumar"}, ...}。FIX:更改代码为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"]]
让·弗朗索瓦·比彻姆

PS result不是OP请求的哈希。
让·弗朗索瓦·比彻姆

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 Spread + 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
完美,正是我所需要的;)
皮埃尔(Pierre

1
const hash = Object.assign({}, ...(<{}>array.map(s => ({[s.key]: s.value}))));必须进行此更改才能使用打字稿。
ruwan800

24

使用价差运算符:

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

jsFiddle上的代码片段的演示


7
我正因为这个而在这里!散布算子如何再次执行只分配新密钥并返回累加器的常规旧方法?由于它每次都会创建一个新副本,因此传播效果会很差
AMTourky

1
现在,您会在每次迭代中传播。在减速器中突变应该是安全的。```const result = arr.reduce((accumulator,target)=> {accumulator [target.key]:target.val; return accumulator},{}); ```
MTJ

17

您可以使用Array.prototype.reduce()和实际的JavaScript Map来代替JavaScript Object

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

Map和Object有什么区别?
以前,在JavaScript中实现Map之前,由于对象的结构相似,因此Object被用作Map。
根据您的用例,如果您需要有序的键,需要访问地图的大小或需要频繁从地图中添加和删除地图,则最好使用Map。

来自MDN文档的引用:
对象与Maps相似,两者都可以让您将键设置为值,检索这些值,删除键以及检测是否在键处存储了某些内容。因此(并且由于没有内置的替代方法),对象在历史上一直被用作地图。但是,在某些情况下,最好使用Map进行一些重要的区别:

  • 对象的键是字符串和符号,而它们的键可以是Map的任何值,包括函数,对象和任何基元。
  • Map中的键是有序的,而添加到对象的键则没有顺序。因此,在其上进行迭代时,Map对象将按插入顺序返回键。
  • 您可以使用size属性轻松获得Map的大小,而Object中属性的数量必须手动确定。
  • Map是可迭代的,因此可以直接进行迭代,而对Object进行迭代需要以某种方式获取其键并对其进行迭代。
  • 对象具有原型,因此如果您不小心,地图中的默认键可能会与您的键冲突。从ES5开始,可以通过使用map = Object.create(null)绕过此方法,但这很少完成。
  • 在涉及频繁添加和删除密钥对的情况下,Map的性能可能更好。

1
您缺少箭头。更改(mapAccumulator, obj) {...}(mapAccumulator, obj) => {...}
mayid


12

es2015版本:

const myMap = new Map(objArray.map(obj => [ obj.key, obj.val ]));

4

这就是我在TypeScript中所做的事情,我有一个小utils库,我在其中放置了这样的内容

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')

如果要使用对象作为键,则必须使用Map而不是Object,因为打字稿不会允许您将对象用作键
Dany Dhondt

3

如其他张贴者所解释的,有更好的方法可以做到这一点。但是,如果我想坚持使用纯JS和ol'的方式,那么这里是:

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

我喜欢这种方法。我认为有时候最简单的代码是最好的。如今,人们被可变性拒之门外,但是只要包含了可变性,可变性实际上就非常强大和高效。
路易斯·阿西图诺


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(...)是毫无意义的,因为您未在其中返回任何内容?在这种情况下,我建议forEach。
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

以下是我在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 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 2014年

嗯,我明白了。我最初以为这是一个建议的答案:)
jmar777
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.