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

Эффективно переименовывать/перегруппировать javascript/json-объекты в массиве объектов

У меня есть некоторые структурированные данные JSON, например. Предположим, что это взаимозаменяемо, через JSON.parse():

[
    {
        "title": "pineapple",
        "uid": "ab982d34c98f"
    },
    {
        "title": "carrots",
        "uid": "6f12e6ba45ec"
    }
]

Мне нужно, чтобы это выглядело так: переназначение title на name и uid на id с результатом:

[
    {
        "name": "pineapple",
        "id": "ab982d34c98f"
    },
    {
        "name": "carrots",
        "id": "6f12e6ba45ec"
    }
]

Самый очевидный способ сделать это:

str = '[{"title": "pineapple","uid": "ab982d34c98f"},{"title": "carrots", "uid": "6f12e6ba45ec"}]';

var arr = JSON.parse(str);
for (var i = 0; i<arr.length; i++) {
    arr[i].name = arr[i].title;
    arr[i].id = arr[i].uid;
    delete arr[i].title;
    delete arr[i].uid;
}

Fiddle

... или используя что-то более сложное (хотя и не более эффективное), например .

Это все прекрасно и денди, но что, если в массиве было 200 000 объектов? Это большая часть затрат на обработку.

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

4b9b3361

Ответ 1

Как я уже упоминал в комментариях, если вы можете сделать определенные предположения о значениях объектов, вы можете использовать регулярное выражение для замены ключей, например:

str = str.replace(/"title":/g, '"name":');

Это не как "чистый", но может ускорить выполнение задания.


Если вы все равно должны разбирать JSON, более структурированный подход будет заключаться в передать функцию reviver на JSON.parse, и вы можете быть в состоянии избежать дополнительного прохода по массиву. Вероятно, это зависит от того, как работает механизм JSON.parse (возможно, сначала они анализируют всю строку, а затем выполняют второй проход с функцией reviver, и в этом случае вы не получите никаких преимуществ).

var arr = JSON.parse(str, function(prop, value) {
   switch(prop) {
     case "title":
        this.name = value;
        return;
     case "uid":
        this.id = value;
        return;
     default:
        return value;
   }
});

Тесты, используя Node.js script ниже, чтобы протестировать 3 раза:

1389822740739: Beginning regex rename test
1389822740761: Regex rename complete
// 22ms, 22ms, 21ms
1389822740762: Beginning parse and remap in for loop test
1389822740831: For loop remap complete
// 69ms, 68ms, 68ms
1389822740831: Beginning reviver function test
1389822740893: Reviver function complete
// 62ms, 61ms, 60ms

Похоже, что регулярное выражение (в данном случае) является наиболее эффективным, но будьте осторожны при попытке проанализировать JSON с регулярными выражениями.


Тестирование script, загрузка 100 230 строк образца OP JSON:

fs = require('fs');
fs.readFile('test.json', 'utf8', function (err, data) {
    if (err) {
        return console.log(err);
    }
    console.log(new Date().getTime() + ": Beginning regex rename test");
    var str = data.replace(/"title":/g, '"name":');
    str = str.replace(/"uid":/g, '"id":');
    JSON.parse(str);
    console.log(new Date().getTime() + ": Regex rename complete");
    console.log(new Date().getTime() + ": Beginning parse and remap in for loop test");
    var arr = JSON.parse(data);
    for (var i = 0; i < arr.length; i++) {
        arr[i].name = arr[i].title;
        arr[i].id = arr[i].uid;
        delete arr[i].title;
        delete arr[i].uid;
    }
    console.log(new Date().getTime() + ": For loop remap complete");
    console.log(new Date().getTime() + ": Beginning reviver function test");
    var arr = JSON.parse(data, function (prop, value) {
        switch (prop) {
            case "title":
                this.name = value;
                return;
            case "uid":
                this.id = value;
                return;
            default:
                return value;
        }
    });
    console.log(new Date().getTime() + ": Reviver function complete");
});

Ответ 2

Заданный вопрос давным-давно, и с тех пор я привык использовать Array.prototype.map() для получить работу, больше для стабильности и чистоты кода, чем производительности. Хотя он, конечно, не самый результативный, он выглядит великолепно:

var repl = orig.map(function(obj) {
    return {
        name: obj.title,
        id: obj.uid
    }
})

Если вам нужна более гибкая (и ES6-совместимая функция), попробуйте:

let replaceKeyInObjectArray = (a, r) => a.map(o => 
    Object.keys(o).map((key) => ({ [r[key] || key] : o[key] })
).reduce((a, b) => Object.assign({}, a, b)))

например.

const arr = [{ abc: 1, def: 40, xyz: 50 }, { abc: 1, def: 40, xyz: 50 }, { abc: 1, def: 40, xyz: 50 }]
const replaceMap = { "abc": "yyj" }

replaceKeyInObjectArray(arr, replaceMap)

/*
[
    {
        "yyj": 1,
        "def": 40,
        "xyz": 50
    },
    {
        "yyj": 1,
        "def": 40,
        "xyz": 50
    },
    {
        "yyj": 1,
        "def": 40,
        "xyz": 50
    }
]
*/

Ответ 3

Здесь еще раз возьмем предложение OP использовать map() для ясности (не производительности).

var newItems = items.map(item => ({
    name: item.title,
    id: item.uid
}));

Здесь используются функции ES6 arrow и синтаксисы ярлыков, которые возможны, когда в функцию передается только одно парм и только одно утверждение в теле функции.

В зависимости от вашей истории с лямбда-выражениями на разных языках эта форма может или не может резонировать с вами.

Будьте внимательны при возврате литерала объекта в синтаксисе ярлыка функции стрелки, подобном этому. Не забывайте, что дополнительные parens вокруг объектного литерала!

Ответ 4

Если вы хотите сделать его еще более многоразовым. Возможно, это достойный подход.

function rekey(arr, lookup) {
	for (var i = 0; i < arr.length; i++) {
		var obj = arr[i];
		for (var fromKey in lookup) {
			var toKey = lookup[fromKey];
			var value = obj[fromKey];
			if (value) {
				obj[toKey] = value;
				delete obj[fromKey];
			}
		}
	}
	return arr;
}

var arr = [{ apple: 'bar' }, { apple: 'foo' }];
var converted = rekey(arr, { apple: 'kung' });
console.log(converted);

Ответ 5

var jsonObj = [/*sample array in question*/   ]

Основываясь на разных тестах, обсуждаемых ниже, самое быстрое решение является родным для:

var arr = [];
for(var i = 0, len = jsonObj .length; i < len; i++) {
  arr.push( {"name": jsonObj[i].title, "id" : jsonObj[i].uid});
}

Я думаю, что альтернативно без использования фреймворков это будет вариант 2:

var arr = []
jsonObj.forEach(function(item) { arr.push({"name": item.title, "id" : item.uid }); });

Всегда существует дискуссия между использованием навигационных и не-навигационных функций. Если я правильно помню lodash утверждал, что они были быстрее, чем подчеркивание, потому что используют неродные функции для операций с ключами.

Однако в разных браузерах иногда возникают разные результаты. Я всегда искал лучший средний показатель.

Для тестов вы можете взглянуть на это:

http://jsperf.com/lo-dash-v1-1-1-vs-underscore-v1-4-4/8

Ответ 6

Использование ES6:

const renameFieldInArrayOfObjects = (arr, oldField, newField) => {
  return arr.map(s => {
    return Object.keys(s).reduce((prev, next) => {
      if(next === oldField) { 
        prev[newField] = s[next]
      } else { 
        prev[next] = s[next] 
      }
      return prev
    }, {})
  })
}

Использование ES7:

const renameFieldInArrayOfObjects = (arr, oldField, newField) => {
  return arr.map(s => {
    return Object.keys(s).reduce((prev, next) => {
      return next === oldField
        ? {...prev, [newField]: s[next]}
        : {...prev, [next]: s[next]}
    }, {})
  })
}