【问题标题】:What's the best way (most efficient) to turn all the keys of an object to lower case?将对象的所有键转换为小写的最佳方法(最有效)是什么?
【发布时间】:2012-09-14 09:50:35
【问题描述】:

我想出了

function keysToLowerCase (obj) {
  var keys = Object.keys(obj);
  var n = keys.length;
  while (n--) {
    var key = keys[n]; // "cache" it, for less lookups to the array
    if (key !== key.toLowerCase()) { // might already be in its lower case version
        obj[key.toLowerCase()] = obj[key] // swap the value to a new lower case key
        delete obj[key] // delete the old key
    }
  }
  return (obj);
}

但我不确定 v8 会如何处理它,例如,它真的会删除其他键还是只会删除引用而垃圾收集器稍后会咬我?

另外,我创建了these tests,我希望您可以在此处添加您的答案,以便我们可以看到它们是如何匹配的。

编辑 1: 显然,根据测试,如果我们不检查密钥是否已经是小写字母,它会更快,但除了更快之外,它会通过忽略这一点并创建新的小写密钥而造成更多混乱吗?垃圾收集器会对此感到满意吗?

【问题讨论】:

  • 您可以创建一个新对象而不是修改现有对象吗?您可以跳过所有删除操作。
  • 我认为你的代码是合理的。 “如果我们不检查密钥是否已经是小写,它会更快,但是...如果忽略它并创建新的小写密钥,它会不会造成更多混乱” - 该代码不会t 实际上可以工作(它最终会删除任何已经小写的键),所以它有多快并不重要......
  • @JasonOrendorff 显然速度较慢,并且每次都会创建一个不需要的对象,而垃圾收集器不会对此感到满意...
  • @JoãoPintoJerónimo 您在速度测试中看到的是代码在同一个对象上运行了数千次。当然,测试运行一次之后,就没有更多的工作要做了;所有的键都是小写的。如果您通过使用大量键创建大量对象来测试它:jsperf.com/object-keys-to-lower-case/7,那么所有三种实现都会显着变慢,但在 Firefox 和 Chrome 中创建新对象的速度稍快。
  • 哦,我看到了@JasonOrendorff...我将再次修改测试

标签: javascript performance node.js object garbage-collection


【解决方案1】:

我想出的fastest是如果你创建一个新对象:

var key, keys = Object.keys(obj);
var n = keys.length;
var newobj={}
while (n--) {
  key = keys[n];
  newobj[key.toLowerCase()] = obj[key];
}

我对 v8 的当前内部工作不够熟悉,无法给你一个明确的答案。几年前,我看到一个视频,其中开发人员谈论对象和 IIRC 它只会删除引用并让垃圾收集器处理它。不过那是几年前的事了,就算那时候是这样,现在也不需要这样了。

以后会咬你吗?这取决于你在做什么,但可能不是。创建短期对象是很常见的,因此代码经过优化以处理它。但是每个环境都有其局限性,也许它会咬你。您必须使用实际数据进行测试。

【讨论】:

  • 这是最好的答案,但@JasonOrendorff 也值得称赞。
  • 我认为这需要varkey = keys[n] 前面?
  • @AndrewFerrier 正确。固定。
  • 如果你有类似 {A:1, B : { C: 2 }} A 和 B 会改变但 C 不会改变的东西,这只会做一层
  • @MichaelWarner 没错,它只触及你给它的对象中的键,而不触及孩子。这就是问题所在。看起来你想做一个深拷贝,但那是一个黑暗的深兔子洞......
【解决方案2】:

我会像这样使用Lo-Dash.transform

var lowerObj = _.transform(obj, function (result, val, key) {
    result[key.toLowerCase()] = val;
});

【讨论】:

【解决方案3】:

就个人而言,我会使用:

let objectKeysToLowerCase = function (origObj) {
    return Object.keys(origObj).reduce(function (newObj, key) {
        let val = origObj[key];
        let newVal = (typeof val === 'object') ? objectKeysToLowerCase(val) : val;
        newObj[key.toLowerCase()] = newVal;
        return newObj;
    }, {});
}

简洁,递归处理嵌套对象并返回一个新对象而不是修改原始对象。

在我有限的本地测试中,此函数比当前列出的其他递归解决方案更快(一旦修复)。我很想将它与其他人进行基准测试,但 jsperf 目前处于劣势(???)。

它也是用 ES5.1 编写的,所以根据 MDN 上的文档,应该可以在 FF 4+、Chrome 5+、IE 9.0+、Opera 12+、Safari 5+(所以,几乎所有东西)中运行。

Vanilla JS 的胜利。

我不会太担心这一切的垃圾收集方面。一旦对旧对象的所有引用都被销毁,它将成为 GC,但 new 对象仍将基本上引用它的所有属性,因此它们不会。

任何函数、数组或正则表达式都将通过引用被“复制”。在内存方面,即使是字符串也不会被此过程复制,因为大多数(全部?)现代 JS 引擎用户string interning。我认为剩下的只是数字、布尔值和构成原始结构的对象留给 GC'd。

请注意,如果原始文件具有多个具有相同小写表示的属性,则此过程的(所有实现)将丢失值。即:

let myObj = { xx: 'There', xX: 'can be', Xx: 'only', XX: 'one!' };
console.log(myObj);
// { xx: 'There', xX: 'can be', Xx: 'only', XX: 'one!' }

let newObj = objectKeysToLowerCase(myObj);
console.log(newObj);
// { xx: 'one!' }

当然,有时这正是你想要的。

2018-07-17 更新

有些人注意到原始函数不适用于数组。这是一个扩展的、更有弹性的版本。如果初始值是数组或简单值,它会通过数组正确递归并工作:

let objectKeysToLowerCase = function (input) {
    if (typeof input !== 'object') return input;
    if (Array.isArray(input)) return input.map(objectKeysToLowerCase);
    return Object.keys(input).reduce(function (newObj, key) {
        let val = input[key];
        let newVal = (typeof val === 'object') && val !== null ? objectKeysToLowerCase(val) : val;
        newObj[key.toLowerCase()] = newVal;
        return newObj;
    }, {});
};

【讨论】:

  • 我建议这个解决方案,因为它是一个纯函数并且不会改变原始对象
  • 当键的值是值的数组时,这将不起作用。type of 会将数组作为对象。@Molomby
  • 我喜欢你的解决方案,但如果值是一个数组,它就不起作用。建议修改条件以检查该值是数组类型还是非对象类型。例如let newVal = ( typeof val !== 'object') || ( Array.isArray( val) )? val: objectKeysToLowerCase(val);
  • 我正在使用您的解决方案,但它不适用于 values = null 的属性。我已将if (typeof input !== 'object') return input; 修改为if (typeof input !== 'object' || input===null) return input;
  • 重申上面提到的内容,如果任何对象属性为nulltypeof 检查正确说明undefined),这将引发错误。将|| input == null 添加到第一行检查以解决此问题。
【解决方案4】:

使用Object.fromEntries (ES10)

使用新的Object.fromEntries 方法的本机和不可变解决方案:


const newObj = Object.fromEntries(
  Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])
);

直到那个函数 becomes widely available 你可以用下面的 polyfill 自己定义它:

Object.fromEntries = arr => Object.assign({}, ...Array.from(arr, ([k, v]) => ({[k]: v}) ));

一个好处是这个方法与Object.entries相反,所以现在你可以在对象和数组表示之间来回切换。

【讨论】:

    【解决方案5】:

    ES6 版本:

    Object.keys(source)
      .reduce((destination, key) => {
        destination[key.toLowerCase()] = source[key];
        return destination;
      }, {});
    

    【讨论】:

      【解决方案6】:

      loDash/fp 方式,相当不错,因为它本质上是一个单线

      import {
      mapKeys
      } from 'lodash/fp'
      
      export function lowerCaseObjectKeys (value) {
      return mapKeys(k => k.toLowerCase(), value)
      }
      

      【讨论】:

      • 参数混合
      【解决方案7】:

      在我的测试中使用 forEach 似乎要快一些——而且原来的参考已经消失了,所以删除新的参考会让 g.c. 接触到它。

      function keysToLowerCase(obj){
          Object.keys(obj).forEach(function (key) {
              var k = key.toLowerCase();
      
              if (k !== key) {
                  obj[k] = obj[key];
                  delete obj[key];
              }
          });
          return (obj);
      }
      

      var O={一:1,二:2,三:3,四:4,五:5,六:{a:1,b:2,c:3,D:4,E :5}}; keysToLowerCase(O);

      /* 返回值:(对象)*/

      {
          five:5,
          four:4,
          one:1,
          six:{
              a:1,
              b:2,
              c:3,
              D:4,
              E:5
          },
          three:3,
          two:2
      }
      

      【讨论】:

      • 我在第 9 版中添加了您的功能:jsperf.com/object-keys-to-lower-case/9
      • 仍然没有比我的快...反向 while 循环是 Javascript 中最快的循环,请参阅:jsperf.com/javascript-loop(反向 while 循环会更快,但结果会显示另一件事最快,可能来自之前运行过的旧浏览器)
      • 不是一个纯函数 - 你正在改变参数
      【解决方案8】:

      这是将所有 json 键转换为小写的最简单解决方案。

      let o = {"Account_Number   ":"0102301", "customer_NaME":"name"}
      
      o = Object.keys(o).reduce((c, k) => (c[k.toLowerCase().trim()] = o[k], c), {})
      
      console.log(o)
      

      【讨论】:

        【解决方案9】:

        简化答案

        对于简单的情况,您可以使用以下示例将所有键转换为小写:

        Object.keys(example).forEach(key => {
          const value = example[key];
          delete example[key];
          example[key.toLowerCase()] = value;
        });
        

        您可以使用toUpperCase() 而不是toLowerCase() 将所有键转换为大写:

        Object.keys(example).forEach(key => {
          const value = example[key];
          delete example[key];
          example[key.toUpperCase()] = value;
        });
        

        【讨论】:

          【解决方案10】:

          我使用 ES6 和 TypeScript。 toLowerCaseObject 函数以一个数组为参数,递归地查看对象树并使每个节点小写:

          function toLowerCaseObject(items: any[]) {
                  return items.map(x => {
                      let lowerCasedObject = {}
                          for (let i in x) {
                              if (x.hasOwnProperty(i)) {
                                  lowerCased[i.toLowerCase()] = x[i] instanceof Array ? toLowerCaseObject(x[i]) : x[i];
                              }
                      }
                      return lowerCasedObject;
                  });
              }
          

          【讨论】:

            【解决方案11】:

            单行(仅适用于顶级键):

            Object.assign(...Object.keys(obj).map(key => ({[key.toLowerCase()]: obj[key]})))
            

            转换:

            { a: 1, B: 2, C: { Z: 4 } }
            

            收件人:

            { a: 1, b: 2, c: { Z: 4 } }
            

            【讨论】:

            • 这会创建包含所有属性的数组?
            • 刚刚更新了代码。它将所有顶级键转换为小写。
            【解决方案12】:

            使用 TypeScript

            /**
             * Lowercase the keys of an object
             * @example
              lowercaseKeys({FOO: true, bAr: false}); // {foo: true, bar: false}
             */
            export function lowercaseKeys<T>(object: { [key: string]: T }): { [key: string]: T } {
              const result: { [key: string]: T } = {};
            
              for (const [key, value] of Object.entries(object)) {
                result[key.toLowerCase()] = value;
              }
            
              return result;
            }
            

            用法

            lowercaseKeys({FOO: true, bAr: false}); // {foo: true, bar: false}
            

            【讨论】:

              【解决方案13】:

              这不是最干净的方法,但它对我的团队很有效,因此值得分享。

              我创建了这个方法,因为我们的后端正在运行一种不区分大小写的语言,并且数据库和后端会产生不同的关键情况。对我们来说,它完美无缺。请注意,我们将日期作为字符串发送,我们不发送函数。

              我们已将其缩减为这一行。

              const toLowerCase = (data) => JSON.parse(JSON.stringify(data).replace(/"([^"]+)":/g, ($0, key) => '"' + key.toString().toLowerCase() + '":'))
              

              我们使用JSON.parse(JSON.stringify(obj)) 方法克隆对象。这将生成 JSON 格式的对象的字符串版本。虽然对象是字符串形式,但您可以使用正则表达式,因为 JSON 是一种可预测的格式来转换所有键。

              拆开后是这样的。

              const toLowerCase = function (data) {
                return JSON.parse(JSON.stringify(data)
                 .replace(/"([^"]+)":/g, ($0, key) => {
                   return '"' + key.toString().toLowerCase() + '":'
                 }))
              }
              

              【讨论】:

                【解决方案14】:
                const keysToLowerCase = object => {
                  return Object.keys(object).reduce((acc, key) => {
                    let val = object[key];
                    if (typeof val === 'object') {
                      val = keysToLowerCase(val);
                    }
                    acc[key.toLowerCase()] = val;
                    return acc;
                  }, {});
                };
                

                适用于嵌套对象。

                【讨论】:

                  【解决方案15】:

                  只考虑一次小写,将其存储在 lowKey var:

                  function keysToLowerCase (obj) {
                    var keys = Object.keys(obj);
                    var n = keys.length;
                    var lowKey;
                    while (n--) {
                      var key = keys[n];
                      if (key === (lowKey = key.toLowerCase()))
                      continue
                  
                      obj[lowKey] = obj[key]
                      delete obj[key]
                  
                    }
                    return (obj);
                  }
                  

                  【讨论】:

                  【解决方案16】:

                  这是我基于上述示例之一的递归版本。

                  //updated function
                  var lowerObjKeys = function(obj) {
                    Object.keys(obj).forEach(function(key) {
                      var k = key.toLowerCase();
                      if (k != key) {
                        var v = obj[key]
                        obj[k] = v;
                        delete obj[key];
                  
                        if (typeof v == 'object') {
                          lowerObjKeys(v);
                        }
                      }
                    });
                  
                    return obj;
                  }
                  
                  //plumbing
                  console = {
                    _createConsole: function() {
                      var pre = document.createElement('pre');
                      pre.setAttribute('id', 'console');
                      document.body.insertBefore(pre, document.body.firstChild);
                      return pre;
                    },
                    info: function(message) {
                      var pre = document.getElementById("console") || console._createConsole();
                      pre.textContent += ['>', message, '\n'].join(' ');
                    }
                  };
                  
                  //test case
                  console.info(JSON.stringify(lowerObjKeys({
                    "StackOverflow": "blah",
                    "Test": {
                      "LULZ": "MEH"
                    }
                  }), true));

                  请注意,它不跟踪循环引用,因此您最终可能会出现无限循环,从而导致堆栈溢出。

                  【讨论】:

                  • 这里有个bug;该函数仅重复出现if (k != key),因此如果您将嵌套对象存储在小写键中,它将不会被触及。解决方法是将内部if (typeof v == 'object') { ... } 子句移到if (k != key) { ... } 子句之外。性能看起来差不多。
                  • 哦,还有 var v = obj[key] 分配,确实会降低性能.. :/
                  【解决方案17】:

                  对于所有值:

                  to_lower_case = function(obj) {
                      for (var k in obj){
                          if (typeof obj[k] == "object" && obj[k] !== null)
                              to_lower_case(obj[k]);
                          else if(typeof obj[k] == "string") {
                              obj[k] = obj[k].toLowerCase();
                          }
                      }
                      return obj;
                  }
                  

                  同样可以用于稍作调整的键。

                  【讨论】:

                    【解决方案18】:

                    这就是我的做法。我的输入可以是任何东西,它通过嵌套对象和对象数组进行回避。

                    const fixKeys = input => Array.isArray(input)
                      ? input.map(fixKeys)
                      : typeof input === 'object'
                      ? Object.keys(input).reduce((acc, elem) => {
                          acc[elem.toLowerCase()] = fixKeys(input[elem])
                          return acc
                        }, {})
                      : input
                    

                    使用 mocha 测试

                    const { expect } = require('chai')
                    
                    const fixKeys = require('../../../src/utils/fixKeys')
                    
                    describe('utils/fixKeys', () => {
                      const original = {
                        Some: 'data',
                        With: {
                          Nested: 'data'
                        },
                        And: [
                          'an',
                          'array',
                          'of',
                          'strings'
                        ],
                        AsWellAs: [
                          { An: 'array of objects' }
                        ]
                      }
                    
                      const expected = {
                        some: 'data',
                        with: {
                          nested: 'data'
                        },
                        and: [
                          'an',
                          'array',
                          'of',
                          'strings'
                        ],
                        aswellas: [{ an: 'array of objects' }]
                      }
                    
                      let result
                    
                      before(() => {
                        result = fixKeys(original)
                      })
                    
                      it('left the original untouched', () => {
                        expect(original).not.to.deep.equal(expected)
                      })
                    
                      it('fixed the keys', () => {
                        expect(result).to.deep.equal(expected)
                      })
                    })
                    

                    【讨论】:

                      【解决方案19】:
                      var aa = {ID:1,NAME:'Guvaliour'};
                      var bb= {};
                      var cc = Object.keys(aa);
                      cc.forEach(element=>{
                       bb[element.toLowerCase()]=aa[element];
                      });
                      cosole.log(bb)
                      

                      【讨论】:

                        【解决方案20】:

                        以下代码将所有键转换为小写

                        array.forEach(item=>{
                                 
                                  let data = Object.keys(item).reduce((result, p) => (result[p.toLowerCase().trim()] = item[p], result), {})
                                  
                                  if(data.hasOwnProperty(fieldname)){
                                      if(data[fieldname]){
                                        if(!response['data'].includes(data[fieldname].toLowerCase()))
                                            response['data'].push(data[fieldname]) 
                                        
                                     }
                                   }
                                  
                                })
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2015-01-09
                          • 1970-01-01
                          • 1970-01-01
                          • 2020-01-15
                          • 2021-10-24
                          相关资源
                          最近更新 更多