it-swarm.com.de

Was ist der beste (effizienteste) Weg, alle Schlüssel eines Objekts in Kleinbuchstaben umzuwandeln?

Ich habe mir ausgedacht

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

Aber ich bin nicht sicher, wie sich V8 damit verhalten wird. Wird es beispielsweise die anderen Schlüssel wirklich löschen, oder werden nur die Referenzen gelöscht und der Speicherbereiniger wird mich später beißen?

Außerdem habe ich diese Tests erstellt. Ich hoffe, Sie könnten Ihre Antwort dort hinzufügen, damit wir sehen können, wie sie zusammenpassen.

EDIT 1: Anscheinend ist es den Tests zufolge schneller, wenn wir nicht prüfen, ob der Schlüssel bereits in Kleinbuchstaben geschrieben ist. Wenn Sie jedoch schneller sind, wird dies mehr Unordnung schaffen, indem Sie dies ignorieren und nur erstellen neue Kleinbuchstaben? Wird der Müllsammler damit zufrieden sein?

45

Das am schnellsten , das ich finde, ist, wenn Sie ein neues Objekt erstellen:

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

Ich bin mit der aktuellen inneren Arbeitsweise von Version 8 nicht vertraut, um Ihnen eine endgültige Antwort zu geben. Vor ein paar Jahren habe ich ein Video gesehen, in dem die Entwickler über Objekte sprachen, und IIRC .__ löscht nur die Referenzen und lässt den Speicherbereiniger dafür sorgen. Aber es war Jahre her, auch wenn es damals so war, muss es jetzt nicht so sein.

Wird es dich später beißen? Es hängt davon ab, was Sie tun, aber wahrscheinlich nicht. Es ist üblich, kurzlebige Objekte zu erstellen, damit der Code für die Verarbeitung optimiert wird. Aber jede Umgebung hat ihre Grenzen, und vielleicht beißt sie dich. Sie müssen mit tatsächlichen Daten testen. 

Update 2017:

Es wurde eine Dienstprogrammfunktion hinzugefügt, mit der eine flache oder tiefe Kopie eines Objekts erstellt werden kann, die Zirkelverweise unterstützt. Nur kurz am Knoten getestet.

/** @summary objectKeysToLowerCase( input, deep, filter )
  * returns a new object with all own keys converted to lower case.
  * The copy can be shallow (default) or deep.
  *
  * Circular references is supported during deep copy and the output will have
  * the same structure.
  *
  * By default only objects that have Object as constructor is copied.
  * It can be changed with the "filter"-function.
  *
  * NOTE: If an object has multiple keys that only differs in case,
  * only the value of the last seen key is saved. The order is usually
  * in the order that the keys where created.
  * Exaple : input =  {aa:1, aA:2, Aa:3, AA:4}, output = {aa:4};
  *
  * NOTE: To detect circular references, the list of objects already converted
  * is searched for every new object. If you have too many objects, it will
  * be slower and slower...
  *
  * @param {object} input
  *   The source object
  * @param {boolean|number} deep
  *   A shallow copy is made if "deep" is undefined, null, false or 0.
  *   A deep copy is made if "deep" is true or a positive number.
  *   The number specifies how many levels to copy. Infinity is a valid number.
  *   This variable is used internally during deep copy.
  * @param {function} filter
  *   A filter function(object) to filter objects that should be copied.
  *   If it returns true, the copy is performed.
  * @returns {object}
  *
  */
function objectKeysToLowerCase( input, deep, filter ) {
  var idx, key, keys, last,  output, self, type, value;
  self = objectKeysToLowerCase;
  type = typeof deep;

  // Convert "deep" to a number between 0 to Infinity or keep special object.
  if ( type === 'undefined' || deep === null || deep === 0 || deep === false ) {
    deep = 0; // Shallow copy
  }
  else if ( type === 'object' ) {
    if ( !( deep instanceof self ) ) {
      throw new TypeError( 'Expected "deep" to be a special object' );
    }
  }
  else if ( deep === true ) {
    deep = Infinity; // Deep copy
  }
  else if ( type === 'number' ) {
    if ( isNaN(deep) || deep < 0 ) {
      throw new RangeError(
        'Expected "deep" to be a positive number, got ' + deep
      );
    }
  }
  else throw new TypeError(
    'Expected "deep" to be a boolean, number or object, got "' + type + '"'
  );


  // Check type of input, and throw if null or not an object.
  if ( input === null || typeof input !== 'object' ) {
    throw new TypeError( 'Expected "input" to be an object' );
  }

  // Check type of filter
  type = typeof filter;
  if ( filter === null || type === 'undefined' || type === 'function' ) {
    filter = filter || null;
  } else {
    throw new TypeError( 'Expected "filter" to be a function' );
  }

  keys = Object.keys(input); // Get own keys from object
  last = keys.length - 1;
  output = {}; // new object

  if (deep) { // only run the deep copy if needed.
    if (typeof deep === 'number') {
      // Create special object to be used during deep copy
      deep =
        Object.seal(
          Object.create(
            self.prototype,
            {
              input   : { value : [] },
              output  : { value : [] },
              level   : { value : -1, writable:true },
              max     : { value : deep, writable:false }
            }
          )
        );
    } else {
      // Circle detection
      idx = deep.input.indexOf( input );
      if ( ~idx ) {
        return deep.output[ idx ];
      }
    }

    deep.level += 1;
    deep.input.Push( input );
    deep.output.Push( output );

    idx = last + 1;
    while ( idx-- ) {
      key = keys[ last - idx ]; // Using [last - idx] to preserve order.
      value = input[ key ];
      if ( typeof value === 'object' && value && deep.level < deep.max ) {
        if ( filter ? filter(value) : value.constructor === Object ) {
          value = self( value, deep, filter );
        }
      }
      output[ key.toLowerCase() ] = value;
    }
    deep.level -= 1;
  } else {
    // Simple shallow copy
    idx = last + 1;
    while ( idx-- ) {
      key = keys[ last - idx ]; // Using [last - idx] to preserve order.
      output[ key.toLowerCase() ] = input[ key ];
    }
  }
  return output;
}
48
some

Ich würde Lo-Dash.transform so verwenden:

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

Persönlich würde ich verwenden:

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

Es ist prägnant, wiederholt verschachtelte Objekte und gibt ein neues Objekt zurück, anstatt das Original zu ändern.

In meinen begrenzten lokalen Tests ist diese Funktion schneller als die andere derzeit aufgelistete rekursive Lösung (einmal behoben). Ich würde es gerne mit den anderen messen, aber jsperf ist im Moment (???).

Es ist auch in ES5.1 geschrieben, so dass es laut den Dokumenten zu MDN in FF 4+, Chrome 5+, IE 9.0+, Opera 12+, Safari 5+ (so ziemlich alles) funktionieren sollte.

Vanilla JS für den Sieg.

Ich würde mir nicht allzu viele Sorgen um den Müllsammelaspekt machen. Sobald alle Verweise auf das alte Objekt gelöscht wurden, werden es GCs sein, aber das neu-Objekt verweist im Wesentlichen auf alle seine Eigenschaften.

Alle Funktionen, Arrays oder RegExp werden durch Verweis "kopiert". In Bezug auf den Speicher werden sogar Strings durch diesen Prozess nicht dupliziert, da die meisten (alle?) Modernen JS-Engines string interning . Ich denke, dass nur die Zahlen, die Booleschen und die Objekte, die die ursprüngliche Struktur bildeten, übrig blieben, um GC zu sein.

Beachten Sie, dass (alle Implementierungen von) dieser Prozess an Werten verliert, wenn das Original mehrere Eigenschaften mit derselben Kleinbuchstaben-Darstellung hat. Dh:

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

Natürlich ist es manchmal genau das, was Sie wollen.

Update 2018-07-17

Ein paar Leute haben bemerkt, dass die ursprüngliche Funktion mit Arrays nicht gut funktioniert. Hier ist eine erweiterte, widerstandsfähigere Version. Es wiederholt sich korrekt durch Arrays und funktioniert, wenn der Anfangswert ein Array oder ein einfacher Wert ist:

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

Die LoDash/FP-Methode, ganz nett, wie es im Wesentlichen ein One-Liner ist

import {
mapKeys
} from 'lodash/fp'

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

Mit Object.fromEntries (ES.next)

Native und unveränderliche Lösung mit dem neuen Object.fromEntries Methode:


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

Bis diese Funktion allgemein verfügbar ist, können Sie sie mit der folgenden ES.next-Polyfüllung selbst definieren:

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

Eine nette Sache ist, dass diese Methode das Gegenteil von Object.entries , damit Sie jetzt zwischen der Objekt- und der Array-Darstellung hin und her wechseln können.

4
Yves M.

Die Verwendung von forEach scheint in meinen Tests etwas schneller zu sein - und die ursprüngliche Referenz ist nicht mehr vorhanden. Wenn Sie den neuen löschen, wird der Zugriff auf das 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, zwei: 2, drei: 3, VIER: 4, fünf: 5, SIX: {a: 1, b: 2, c: 3, D: 4, E: 5}} ; keysToLowerCase (O);

/ * Rückgabewert: (Object) * /

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

ES6-Version:

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

Ich habe es auf ähnliche Weise beantwortet, aber ich benutze ES6 und TypeScript. Die toLowerCaseObject-Funktion nimmt ein Array als Parameter und es rekursiv durch den JSON-Baum und macht jeden Knoten in Kleinbuchstaben:

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

Erwägen Sie, die Groß-/Kleinschreibung nur einmal zu verringern, und speichern Sie sie in einer 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

So mache ich es. Meine Eingabe kann alles sein und wird durch verschachtelte Objekte sowie durch Arrays von Objekten wiederverwendet.

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

getestet mit Mokka

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

Vereinfachte Antwort

In einfachen Situationen können Sie das folgende Beispiel verwenden, um alle Schlüssel in Kleinbuchstaben zu konvertieren:

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

Sie können alle Tasten mit toUpperCase() anstelle von toLowerCase() in Großbuchstaben konvertieren

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

Für alle Werte:

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

Dasselbe kann für Tasten mit geringfügigen Änderungen verwendet werden.

0
blockwala

Hier ist meine rekursive Version basierend auf einem der obigen Beispiele.

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

Achtung, es werden keine kreisförmigen Referenzen erfasst, sodass Sie eine Endlosschleife erhalten, die zu einem Stapelüberlauf führt.

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

Dies ist nicht der sauberste Weg, aber es hat für mein Team funktioniert, also lohnt es sich, es zu teilen.

Ich habe diese Methode erstellt, da in unserem Backend eine Sprache ausgeführt wird, die nicht zwischen Groß- und Kleinschreibung unterscheidet, und die Datenbank und das Backend unterschiedliche Fälle erzeugen. Für uns hat es einwandfrei funktioniert. Beachten Sie, dass wir Datumsangaben als Strings senden und keine Funktionen senden.

Wir haben es auf diese eine Zeile reduziert.

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

Wir klonen das Objekt mit der JSON.parse(JSON.stringify(obj))-Methode. Dies erzeugt eine String-Version des Objekts im JSON-Format. Während sich das Objekt im String-Format befindet, können Sie Regex verwenden, da JSON ein vorhersagbares Format zum Konvertieren aller Schlüssel ist.

Zerbrochen sieht es so aus.

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