web-dev-qa-db-ja.com

オブジェクトのすべてのキーを小文字に変換する最良の方法(最も効率的な)は何ですか?

思いついた

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がそれでどのように動作するのかわかりません、たとえば、他のキーを本当に削除するのか、参照を削除するだけで、ガベージコレクタは後で私を噛みますか?

また、 これらのテスト を作成しました。そこに答えを追加して、それらがどのように一致するかを確認できることを望んでいます。

EDIT 1:どうやら、テストによると、キーがすでに小文字であるかどうかをチェックしないほうが速いですが、脇に置いて高速にすると、これを無視して、新しい小文字のキーを作成するだけで、混乱が生じますか?ガベージコレクターはこれに満足しますか?

56

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は参照を削除するだけで、ガベージコレクターが処理します。しかし、それは数年前だったので、たとえそのようだったとしても、今はそのようである必要はありません。

後で噛むのでしょうか?それはあなたが何をしているかに依存しますが、おそらくそうではありません。コードがそれを処理するために最適化されるように、短命のオブジェクトを作成することは非常に一般的です。しかし、すべての環境には限界があり、おそらくあなたに噛み付くでしょう。実際のデータでテストする必要があります。

56
some

Lo-Dash.transform を次のように使用します。

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

個人的に、私は使用します:

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+(したがって、ほとんどすべて)。

勝利のためのバニラJS。

このすべてのガベージコレクションの側面についてはあまり心配しません。古いオブジェクトへのすべての参照が破棄されるとGCになりますが、newオブジェクトは基本的にすべてのプロパティを参照するため、そうではありません。

関数、配列、またはRegExpは、参照によって「コピー」されます。メモリに関しては、ほとんどの(すべて?)最新のJSエンジンユーザー string interning であるため、文字列でさえこのプロセスによって複製されません。 GCに残された元の構造を形成したNumbers、Booleans、Objectsだけが残っていると思います。

オリジナルに同じ小文字表現を持つ複数のプロパティがある場合、このプロセス(のすべての実装)は値を失うことに注意してください。すなわち:

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

LoDash/fpの方法、本質的に1つのライナーとして非常にいい

import {
mapKeys
} from 'lodash/fp'

export function lowerCaseObjectKeys (value) {
return mapKeys(k => k.toLowerCase(), value)
}
6
Damian Green

Object.fromEntries(ES.next)を使用する

新しい Object.fromEntries メソッドを使用したネイティブで不変のソリューション:


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

その関数が広く利用可能になるまで、次のES.nextポリフィルを使用して自分で定義できます。

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

良い点は、このメソッドが Object.entries の逆を行うため、オブジェクトと配列表現の間を行き来できることです。

4
Yves M.

ES6バージョン:

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

私のテストでは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 = {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
}
3
kennebec

簡略化された回答

単純な状況では、次の例を使用してすべてのキーを小文字に変換できます。

_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;
});
_
1
Grant Miller

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;
        });
    }
1
El.

大文字小文字を一度だけ減らして、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);
}
0
moonwave99

これが私のやり方です。私の入力は何でもよく、ネストされたオブジェクトとオブジェクトの配列を再利用します。

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
Dave Sag

すべての値の場合:

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
blockwala

上記の例のいずれかに基づいた私の再帰バージョンは次のとおりです。

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

循環参照を追跡しないため、スタックオーバーフローが発生する無限ループになる可能性があることに注意してください。

0
kixorz
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
Ajai

これは最もクリーンな方法ではありませんが、私のチームで機能しているので、共有する価値があります。

バックエンドは大文字と小文字を区別しない言語を実行しており、データベースとバックエンドは異なるキーケースを生成するため、このメソッドを作成しました。私たちにとっては、完璧に機能しました。日付を文字列として送信しますが、関数は送信しません。

これを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() + '":'
   }))
}
_
0
Michael Warner