Подтвердить что ты не робот

Какой лучший способ (наиболее эффективный) превратить все ключи объекта в нижний регистр?

Я придумал

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: По-видимому, согласно испытаниям, это быстрее, если мы не будем проверять, находится ли ключ в нижнем регистре, но, ускоряясь в сторону, создаст ли он больше беспорядка, проигнорировав это и просто создав новые строчные ключи? Будет ли сборщик мусора быть счастливым с этим?

4b9b3361

Ответ 1

Самый быстрый, который я придумаю, - это если вы создаете новый объект:

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

Я недостаточно знаком с текущей внутренней работой v8, чтобы дать вам окончательный ответ. Несколько лет назад я видел видео, где разработчики говорили об объектах, и IIRC он только удалит ссылки и позволит сборщику мусора позаботиться об этом. Но это было много лет назад, так что даже если бы это было так тогда, это не должно быть так сейчас.

Это укусит тебя позже? Это зависит от того, что вы делаете, но, вероятно, нет. Очень часто создаются недолговечные объекты, поэтому код оптимизирован для его обработки. Но у каждой среды есть свои ограничения, и, возможно, она вас укусит. Вы должны проверить с фактическими данными.

Ответ 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, но новый объект по-прежнему будет ссылаться в основном на все его свойства, поэтому они не будут.

Любые функции, массивы или RegExp будут "скопированы" по ссылке. С точки зрения памяти, даже строки не будут дублироваться этим процессом, так как большинство (все?) Современные JS-движки обрабатывают пользовательские строки. Я думаю, что это оставляет только Числа, Булевы и Объекты, которые сформировали оригинальную структуру, оставленными для 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') ? objectKeysToLowerCase(val) : val;
        newObj[key.toLowerCase()] = newVal;
        return newObj;
    }, {});
};

Ответ 4

Метод loDash/fp, довольно приятный, поскольку его по существу один лайнер

import {
mapKeys
} from 'lodash/fp'

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

Ответ 5

Использование 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, поэтому теперь вы можете переходить между объектом и представлением массива.

Ответ 6

Версия ES6:

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

Ответ 7

Использование inEach в моих тестах немного быстрее, и исходная ссылка исчезла, поэтому удаление нового приведет к достижению 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, tree: 3, FOUR: 4, Five: 5, SIX: {a: 1, b: 2, c: 3, D: 4, E: 5}}; keysToLowerCase (О);

/* возвращаемое значение: (Object) */

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

Ответ 8

Я использовал ES6 и TypeScript. Функция toLowerCaseObject принимает Array в качестве параметра и рекурсивно просматривает дерево объектов и делает каждый узел строчным:

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

Ответ 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

Рассмотрим случай спуска только один раз, сохраняя его в 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);
}

Ответ 11

Здесь моя рекурсивная версия, основанная на одном из приведенных выше примеров.

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

Ответ 12

Это не самый чистый способ, но он сработал для моей команды, поэтому стоит поделиться.

Я создал этот метод, так как наш бэкэнд работает на языке, который не чувствителен к регистру, а база данных и бэкэнд будут генерировать разные ключевые случаи. Для нас это сработало без нареканий. Напоминаем, мы отправляем даты в виде строк и не отправляем функции.

Мы сократили это до этой одной строки.

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

Ответ 13

Для всех значений:

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

То же самое можно использовать для ключей с незначительными изменениями.

Ответ 14

Вот как я это делаю. Мой ввод может быть любым, и он находит через вложенные объекты, а также массивы объектов.

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

Ответ 15

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