【问题标题】:Convert object array to hash map, indexed by an attribute value of the Object将对象数组转换为哈希映射,由对象的属性值索引
【发布时间】:2014-12-03 14:08:03
【问题描述】:

用例

用例是将对象数组转换为基于提供的字符串或函数的哈希映射,以评估并用作哈希映射中的键,并将值用作对象本身。使用它的一个常见情况是将对象数组转换为对象的哈希映射。

代码

以下是JavaScript中的一个小sn-p,用于将对象数组转换为哈希映射,由对象的属性值索引。您可以提供一个函数来动态评估哈希映射的键(运行时)。

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

您可以在这里找到要点:Converts Array of Objects to HashMap

【问题讨论】:

标签: javascript arrays hashmap


【解决方案1】:

这对于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+,所以如果您需要支持旧版浏览器,则需要对其进行 polyfill。

【讨论】:

  • result = arr.reduce((map, obj) => (map[obj.key] = obj.val, map), {}); ES6 单线爱好者:D
  • @Mtz 对于 ES6 单线爱好者来说,mateuscb 下面的回复更小更干净:result = new Map(arr.map(obj => [obj.key, obj.val]));。最重要的是,它非常清楚地显示了正在返回的地图。
  • @RyanShillington 我们在这里是一个答案的上下文,即 jmar777 提出的Array.prototype.reduceMap 确实更短,但它是另一回事。我一直在与最初的意图保持一致。请记住,这不是一个论坛,您可能想了解更多关于 SO Q/A 结构的信息。
  • @Mtz 够公平的。
  • 这不是我们要求的,恕我直言。显示的数组的正确结果是:{ "foo": {key: 'foo', val: 'bar'}, "hello": {key: 'hello', val: 'world'} }。请注意,每个原始元素应完整保留。或者使用 Q 的数据:{"345": {id:345, name:"kumar"}, ...}。 FIX:将代码更改为 map[obj.key] = obj;
【解决方案2】:

使用 ES6 Map (pretty well supported),你可以试试这个:

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

【讨论】:

  • 同样重要的是要注意,要从Map 中获取某些内容,您需要使用result.get(keyName) 而不是仅使用result[keyName]。另请注意,任何对象都可以用作键,而不仅仅是字符串。
  • 另一个 TypeScript 版本看起来像这样:var result = new Map(arr.map(i => [i.key, i.val] as [string, string]));,有些人可能会觉得更容易理解。注意 as [string, string] 类型转换添加。
  • 当我在 Chrome v71 中运行这段代码时,我仍然得到一个数组:Result is: [["foo","bar"],["hello","world"]]
  • P.S. result 不是 OP 要求的哈希值。
  • 另一个打字稿版本:var result = new Map<string, string>(arr.map(i => [i.key, i.val]));
【解决方案3】:

您可以使用新的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}

【讨论】:

  • 这比上面的答案更具可读性;但是,它将迭代数组两次(一次用于map 调用,一次用于Object.fromEntries 调用)。
  • @knguyen 是的,我猜。但在大多数情况下,我猜额外的循环无关紧要
【解决方案4】:

使用 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}

【讨论】:

  • 完美,正是我需要的 ;)
  • const hash = Object.assign({}, ...(<{}>array.map(s => ({[s.key]: s.value})))); 必须进行此更改才能使用 typescript。
  • 重要的是要注意这个方法不会产生一个地图而是一个对象
【解决方案5】:

使用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}

【讨论】:

  • 那不是哈希图
【解决方案6】:

使用扩展运算符:

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

jsFiddle 上的代码 sn-p 演示。

【讨论】:

  • 我就是因为这个!扩展运算符如何执行与仅分配新键并返回累加器的常规旧方法相反的操作?因为它每次都会创建一个新副本,所以 spread 将表现不佳
  • 现在你在每次迭代中传播。在减速器中变异应该是安全的。 ``` const result = arr.reduce( (accumulator, target) => { accumulator[target.key]: target.val; return accumulator }, {}); ```
【解决方案7】:

您可以使用 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);

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

引用MDN document:
对象与 Maps 类似,都允许您将键设置为值、检索这些值、删除键以及检测是否在键中存储了某些内容。正因为如此(并且因为没有内置的替代品),对象在历史上一直被用作地图;但是,在某些情况下,使用地图有一些重要的区别:

  • 对象的键是字符串和符号,而它们可以是 Map 的任何值,包括函数、对象和任何原语。
  • Map 中的键是有序的,而添加到对象的键不是。因此,在对其进行迭代时,Map 对象会按插入顺序返回键。
  • 您可以使用 size 属性轻松获取 Map 的大小,而 Object 中的属性数量必须手动确定。
  • Map 是可迭代对象,因此可以直接迭代,而对 Object 进行迭代则需要以某种方式获取其键并对其进行迭代。
  • 一个对象有一个原型,所以如果你不小心的话,映射中的默认键可能会与你的键发生冲突。从 ES5 开始,这可以通过使用 map = Object.create(null) 绕过,但很少这样做。
  • 在涉及频繁添加和删除密钥对的场景中,Map 的性能可能会更好。

【讨论】:

  • 您缺少箭头。将(mapAccumulator, obj) {...} 更改为(mapAccumulator, obj) => {...}
【解决方案8】:

es2015版本:

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

【讨论】:

    【解决方案9】:

    正如其他海报所解释的,有更好的方法来做到这一点。但如果我想坚持纯 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);
    

    【讨论】:

    • 是否建议使用reduce方法而不是这种方法。我觉得用这种方法。它简单易懂,一目了然。
    • 我喜欢这种方法。我认为有时最简单的代码是最好的。现在人们对可变性感到厌烦,但只要它被包含在内,可变性实际上是非常棒的和高性能的。
    • 我使用这个是因为 IE11 不允许带有 Lambda 的那个
    • 感谢您发布唯一具有多个相同键的示例,这是我需要处理的情况。我也喜欢这个,因为正如 Anthosh 所说,您实际上可以看到发生了什么。我对改进这个答案的唯一建议是显示输出
    【解决方案10】:

    如果您想转换为新的 ES6 Map,请执行以下操作:

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

    为什么要使用这种类型的地图?这取决于你。看看this

    【讨论】:

      【解决方案11】:

      这就是我在 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')
      

      【讨论】:

      • 如果你想使用一个对象作为键,你必须使用 Map 而不是 Object,因为 typescript 不允许你使用对象作为键
      • const normalize = (a,f) =&gt; a.reduce((m,o)=&gt;(m[o[f]]=o,m),{});
      【解决方案12】:

      ? 最糟糕的

      list.reduce((obj, item) => (obj[item.name] = item.value, obj), {})
      

      const list = [
        { name: 'abc', value: 123 },
        { name: 'xyz', value: 789 },
        { name: 'she', value: 'her' },
        { name: 'he', value: 'him'}
      ]
        
      console.log(
        // ? last item in comma evaluation is always returned
        list.reduce((obj, item) => (obj[item.name] = item.value, obj), {})
      )

      【讨论】:

        【解决方案13】:

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

        【讨论】:

        • other 答案快吗?
        • @orad 可能不会,因为它会传播累加器并在每次迭代时创建新对象。
        【解决方案14】:

        使用简单的 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
        

        【讨论】:

        • 在此示例中没有使用 .map(...) 毫无意义,因为您没有返回任何内容?在这种情况下,我建议使用 forEach。
        【解决方案15】:

        lodash:

        const items = [
            { key: 'foo', value: 'bar' },
            { key: 'hello', value: 'world' }
        ];
        
        const map = _.fromPairs(items.map(item => [item.key, item.val]));
        
        // OR: if you want to index the whole item by key:
        // const map = _.fromPairs(items.map(item => [item.key, item]));
        

        lodash fromPairs 函数让我想起了zip function in Python

        链接到lodash

        【讨论】:

          【解决方案16】:

          reduce 版本似乎不起作用。我跟着去。

             let map = {};
              items.forEach(v=>{
                map [v.xxx] = v;
              });
          

          【讨论】:

            【解决方案17】:

            试试

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

            let arr=[
              {id:123, name:'naveen'}, 
              {id:345, name:"kumar"}
            ];
            
            let fkey = o => o.id; // function changing object to string (key)
            
            let toHashMap = (a,f) => a.reduce((a,c)=> (a[f(c)]=c,a),{});
            
            console.log( toHashMap(arr,fkey) );
            
            // Adding to prototype is NOT recommented:
            //
            // Array.prototype.toHashMap = function(f) { return toHashMap(this,f) };
            // console.log( arr.toHashMap(fkey) );

            【讨论】:

              【解决方案18】:

              对我来说,我不喜欢使用任何mapreduce,而是坚持使用简单的for 循环。

              const array = [
                 {key: 'a', value: 'b', redundant: 'aaa'},
                 {key: 'x', value: 'y', redundant: 'zzz'}
              ]
              
              const hash = {};
              
              for (const item of array) {
                  hash[item.key] = item;
              }
              
              console.log(hash);
              

              【讨论】:

              • 我同意@Yada。易于阅读和直截了当的代码更适合理智。
              【解决方案19】:

              以下是我在 javascript 中创建的小 sn-p,用于将对象数组转换为哈希映射,由对象的属性值索引。您可以提供一个函数来动态评估哈希映射的键(运行时)。

              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

              【讨论】:

              • 拜托拜托,请不要推荐扩展Array.prototype
              • 啊,我明白了。我最初认为这是一个建议的答案:)
              【解决方案20】:

              像这样映射数组:

              const items = [
              { key: 'foo', value: 'bar' },
              { key: 'hello', value: 'world' }
              ];
              
              let [k,v] = items.map(item => [item.key, item.value])   
              console.log([k,v]) 
              
              //Output: [ [ 'foo', 'bar' ], [ 'hello', 'world' ] ]
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-05-12
                • 2016-08-02
                • 2015-01-04
                • 2021-11-07
                • 1970-01-01
                • 2019-02-10
                相关资源
                最近更新 更多