Как сделать контрольную сумму из объекта JavaScript?
Мне нужно сделать контрольную сумму из объекта JavaScript.
К сожалению, кажется, что не существует простого способа сделать это из-за упорядочения объектов в JavaScript. Например, возьмите эти объекты:
var obj1 = {type:"cake",quantity:0}
, obj2 = {quantity:0,type:"cake"};
Я считаю эти объекты равными в данных и хотел бы, чтобы их контрольные суммы были одинаковыми. Я действительно не забочусь о порядке Объекта, пока данные в них одинаковы.
Увы, JSON.stringify
оба они на самом деле не равны; как единственный способ сделать контрольную сумму объекта через его представление String, а JSON.stringify
Е-представления не равны, мои контрольные суммы не будут равны!
Одно решение, которое я придумала, - это воссоздание объекта на основе предопределенной схемы, например, так:
var schema = ["type","quantity"];
function sortify(obj,schema){
var n={};
for(i in schema)
n[schema[i]]=obj[schema[i]];
return n
}
Бег JSON.stringify(sortify(obj1,schema))==JSON.stringify(sortify(obj2,schema))
вернусь true
... но ценой создания нового объекта и перетасовки данных.
Мое другое решение состоит в том, чтобы заменить JSON.stringify
метод с одним, который выбирает ключи из предопределенной схемы и систематизирует их значения, а затем соединяет их вместе. Функция читает:
function smarterStringify(obj,schema){
var s="";
for(i in schema)
s+=JSON.stringify(obj[schema[i]]);
return s
}
Игнорирование того факта, что этот метод не возвращает правильный JSON (достаточно близко к примеру того, что я пытаюсь сделать), это значительное улучшение по сравнению с первым по скорости (по крайней мере, в моем браузере Chrome OS вы можете проверить это сами здесь: http://jsperf.com/sort-then-json-stringify-vs-smarter-stringify) и, конечно, это делает два представления Object String равными!
Однако мне было просто интересно, пропустил ли я что-то, и был ли встроенный метод для чего-то подобного, который не а) не приводил GC JavaScript в патологический случай или б) не выполнял слишком много конкатенаций строк. Я бы предпочел не делать этого.
3 ответа
Вы можете собрать ключи в массив с Object.keys()
отсортируйте этот массив и затем проверьте контрольную сумму ключей / значений в этом известном, предсказуемом порядке. Я не знаю, как использовать JSON.stringify()
со всеми отсортированными ключами одновременно, так что вам придется делать свою собственную контрольную сумму.
Я не знаю ни одного встроенного метода для чего-то подобного. Ключи объектов НЕ гарантированно находятся в каком-либо определенном порядке, поэтому полагаться на это небезопасно.
Если у вас нет вложенных объектов или массивов в качестве значений свойств, то вы можете сделать что-то вроде этого:
// creates an array of alternating property name, property value
// with properties in sorted order
// then stringify's that array
function stringifyPropsInOrder(obj) {
var keys = Object.keys(obj).sort();
var output = [], prop;
for (var i = 0; i < keys.length; i++) {
prop = keys[i];
output.push(prop);
output.push(obj[prop]);
}
return JSON.stringify(output);
}
function compareObjects(a, b) {
return stringifyPropsInOrder(a) === stringifyPropsInOrder(b);
}
Если вам нужна более высокая производительность, вам не нужно разбивать на строки (это было сделано здесь для сохранения кода). Вы можете просто вернуть плоский output
массив и сравнить массивы напрямую.
Если бы вы могли иметь встроенные объекты в качестве значений свойств, то нужно еще немного поработать, чтобы рекурсивно развернуть их в один и тот же плоский массив свойств / значений.
3 года спустя...
Я столкнулся с этим вопросом, так как хотел хэшировать свои объекты JSON для создания Etag
s для моих ответов HTTP. В итоге я написал собственное решение для Node, jsum
, который сводится к простому сериализатору:
**
* Stringifies a JSON object (not any randon JS object).
*
* It should be noted that JS objects can have members of
* specific type (e.g. function), that are not supported
* by JSON.
*
* @param {Object} obj JSON object
* @returns {String} stringified JSON object.
*/
function serialize (obj) {
if (Array.isArray(obj)) {
return JSON.stringify(obj.map(i => serialize(i)))
} else if (typeof obj === 'object' && obj !== null) {
return Object.keys(obj)
.sort()
.map(k => `${k}:${serialize(obj[k])}`)
.join('|')
}
return obj
}
Затем вы можете взять результат и хешировать его, используя общие алгоритмы (например, SHA256
) или используйте удобный метод digest
от jsum
пакет.
Пожалуйста, обратите внимание на лицензию здесь!
Вы также можете сделать функцию, которая сравнивает ваши объекты на месте:
function compareObjects(a, b) {
var akeys = Object.keys(a);
var bkeys = Object.keys(b);
var len = akeys.length;
if (len != bkeys.length) return false;
for (var i = 0; i < len; i++) {
if (a[akeys[i]] !== b[akeys[i]]) return false;
}
return true;
}
Это предполагает, что они являются объектами, переданными, и что они являются простыми плоскими объектами. Можно добавить логику, чтобы проверить эти предположения и рекурсивно проверить, равны ли подобъекты.