将对象的所有键都转换为小写的最佳方式(最有效)是什么?


69

我想出了

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的行为如何,例如,它将真正删除其他键还是仅删除引用,然后垃圾收集器稍后会咬我?

另外,我创建了这些测试,希望您可以在此处添加答案,以便我们了解它们的匹配情况。

编辑1: 显然,根据测试,如果我们不检查键是否已经为小写字母,则速度会更快,但撇开速度来看,如果忽略此键而只是创建新的小写键,会不会造成混乱?垃圾收集者对此感到满意吗?


2
您可以创建一个新对象而不是修改现有对象吗?您将跳过所有删除。
杰森·奥伦多夫

我认为您的代码是合理的。“如果我们不检查键是否已经为小写字母,则速度会更快,但是...通过忽略此键并仅创建新的小写键,它会造成更多混乱” -该代码实际上不起作用(它将结束删除已经是小写字母的所有键),因此它的速度有多快都没有关系……
nnnnnn 2012年

@JasonOrendorff显然它的速度较慢,而且会造成每次一个不需要的对象,垃圾收集器不会满意了......
若奥·平托赫罗尼莫

@JoãoPintoJerónimo在速度测试中看到的是,代码在同一个对象上运行了数千次。当然,测试运行一次之后,就没有更多的工作要做了。所有的键都是小写的。如果通过创建带有很多键的对象来测试它,请执行以下操作:jsperf.com/object-keys-to-lower-case/7,那么这三个实现的速度都大大降低,但是在Firefox和Chrome中创建新对象的速度稍快。
杰森·奥伦多夫

哦,我看到@JasonOrendorff ...我会做测试的另一个版本
若奥·平托赫罗尼莫

Answers:


73

我想出的最快的方法是创建新对象:

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

我对v8的当前内部工作还不太熟悉,无法为您提供明确的答案。几年前,我看了一个视频,其中开发人员讨论了对象,而IIRC只会删除引用,让垃圾收集器来处理。但是那是几年前的事,所以即使那时是那样,现在也不必那样。

以后会咬你吗?这取决于您在做什么,但可能并非如此。创建寿命很短的对象非常普遍,因此对代码进行了优化以对其进行处理。但是每种环境都有其局限性,也许会刺痛您。您必须使用实际数据进行测试。


1
这是最好的答案,但是@JasonOrendorff也值得赞扬。
若奥·平托赫罗尼莫

我觉得这需要var前面key = keys[n]吗?
安德鲁·费里尔

@AndrewFerrier正确。固定。
某个

仅当您有{A:1,B:{C:2}}这样的内容时,A和B才会更改,而C则不会更改
Michael Warner

1
@MichaelWarner是的,它仅触摸您赋予它的对象中的键,而不触摸孩子。这就是问题所在。看来您想进行深层复制,但这是一个深深的兔子黑洞……
大约


26

就个人而言,我会使用:

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的,但对象基本上仍将引用其所有属性,因此它们不会。

所有函数,数组或RegExp将通过引用“复制”。就内存而言,由于大多数(全部?)现代JS引擎都使用用户字符串interning,因此即使是String也不会被此过程复制。我认为只剩下构成原始结构的数字,布尔值和对象需要进行GC处理。

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

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') ? objectKeysToLowerCase(val) : val;
        newObj[key.toLowerCase()] = newVal;
        return newObj;
    }, {});
};

1
我建议使用这种解决方案,因为它是一种纯函数,并且不会变异原始对象
Eugene Fidelin

1
当键的值是values.type的数组时,这将无法工作。@ Molomby
VivekN

1
我喜欢您的解决方案,但如果值是数组,则无法使用。建议修改条件以检查该值是数组类型还是非对象类型。例如 let newVal = ( typeof val !== 'object') || ( Array.isArray( val) )? val: objectKeysToLowerCase(val);
安迪·莱

应该强调此解决方案处理嵌套对象的能力,很好的解决方案
Kubie

我正在使用您的解决方案,但不适用于value = null的属性。我已修改if (typeof input !== 'object') return input;if (typeof input !== 'object' || input===null) return input;
Sven Tore

20

使用Object.fromEntries(ES10)

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


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

在该功能广泛可用之前,您可以使用以下polyfill自己定义它:

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

一件好事是,该方法的作用与之相反Object.entries,因此现在您可以在对象和数组表示形式之间来回切换。



7

ES6版本:

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

4

在我的测试中,使用forEach似乎更快一些,并且原来的参考资料消失了,因此删除新的参考资料将使其进入gc

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 = {ONE:1,two:2,tHree:3,FOUR:4,Five:5,SIX:{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
}


1
仍然不比我快。...while反向循环是Javascript中最快的循环,请参见:jsperf.com/javascript-loop(while循环反向会更快,但结果将显示另一件事是最快的,可能是(以前运行过的旧浏览器))
JoãoPintoJerónimo2012年

不是纯函数-您正在改变参数
尤金·菲德林

3

这是将所有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)

2

简化答案

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

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

2

我使用了ES6和TypeScript。 toLowerCaseObject函数将Array作为参数,并递归地浏览Object树并使每个节点小写:

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

1

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

我创建此方法的原因是后端运行的语言不区分大小写,并且数据库和后端将产生不同的关键案例。对于我们来说,它完美地工作了。请注意,我们将日期作为字符串发送,并且不发送函数。

我们将其简化为这一行。

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() + '":'
   }))
}

1
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;
  }, {});
};

适用于嵌套对象。


0

考虑只降低一次大小写,将其存储在lowKeyvar中:

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

加入到版本6:jsperf.com/object-keys-to-lower-case/6这不是更快,我认为
若奥·平托赫罗尼莫

0

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

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

当心,它不会跟踪循环引用,因此您可能会陷入无限循环,从而导致堆栈溢出。


这里有一个错误;该函数仅会重复出现if (k != key),因此,如果您将嵌套对象存储在小写键中,则不会被触摸。解决方法是将内部if (typeof v == 'object') { ... }子句移到该子句之外if (k != key) { ... }。性能看起来差不多。
Molomby

哦,var v = obj[key]任务也是如此,它的确会降低性能。.:/
Molomby

0

对于所有值:

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

可以对细微调整的键使用相同的键。


0

这就是我的方法。我的输入可以是任何东西,并且可以通过嵌套对象和对象数组进行重复使用。

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

使用摩卡测试

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

0
var aa = {ID:1,NAME:'Guvaliour'};
var bb= {};
var cc = Object.keys(aa);
cc.forEach(element=>{
 bb[element.toLowerCase()]=aa[element];
});
cosole.log(bb)

0

一线(仅适用于顶级按键):

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

这会创建具有所有属性的数组吗?
worldwildwebdev

刚刚更新了代码。它将所有顶级键转换为小写。
giladb19年

0

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

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]) 
                
             }
           }
          
        })

0

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