如何从 JavaScript 对象进行校验和?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24917152/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I make a checksum from a JavaScript Object?
提问by striking
I need to make a checksum from a JavaScript Object.
Unfortunately, there does not seem to be an easy way to accomplish this because of JavaScript's Object ordering. For example, take these Objects:
我需要从 JavaScript 对象进行校验和。
不幸的是,由于 JavaScript 的对象排序,似乎没有一种简单的方法可以实现这一点。例如,拿这些对象:
var obj1 = {type:"cake",quantity:0}
, obj2 = {quantity:0,type:"cake"};
I consider these Objects equal in data, and would like their checksums to be the same. I really don't care about the order of the Object just as long as the data in them is the same.
Alas, JSON.stringify
of both of them is actually not equal; as the only way to make a checksum of an Object is via its String representation, and the JSON.stringify
-ed representations are not equal, my checksums will not be equal!
One solution I have come up with is to recreate the Object based on a predefined schema, like so:
我认为这些对象的数据相等,并希望它们的校验和相同。我真的不关心对象的顺序,只要它们中的数据相同即可。
唉,JSON.stringify
两者其实并不相等;由于对对象进行校验和的唯一方法是通过其字符串表示,并且JSON.stringify
-ed 表示不相等,因此我的校验和将不相等!
我想出的一个解决方案是基于预定义的模式重新创建对象,如下所示:
var schema = ["type","quantity"];
function sortify(obj,schema){
var n={};
for(i in schema)
n[schema[i]]=obj[schema[i]];
return n
}
Running JSON.stringify(sortify(obj1,schema))==JSON.stringify(sortify(obj2,schema))
will return true
... but at the price of creating a new Object and shuffling around data.
运行JSON.stringify(sortify(obj1,schema))==JSON.stringify(sortify(obj2,schema))
将返回true
......但代价是创建一个新对象并围绕数据进行混洗。
My other solution is to replace the JSON.stringify
method with one that picks keys from a predefined schema and stringifying their values, then joining them together.
The function reads:
我的另一个解决方案是JSON.stringify
用一个从预定义的模式中选择键并将它们的值字符串化,然后将它们连接在一起的方法替换该方法。该函数写道:
function smarterStringify(obj,schema){
var s="";
for(i in schema)
s+=JSON.stringify(obj[schema[i]]);
return s
}
Ignoring the fact that this method doesn't return correct JSON (it's close enough as an example of what I'm trying to do), it is a massive improvement over the first one in speed (at least in my Chrome OS browser, you can check it yourself here: http://jsperf.com/sort-then-json-stringify-vs-smarter-stringify), and of course it makes the two Object String representations equal!
忽略此方法不返回正确 JSON 的事实(作为我正在尝试做的事情的一个例子,它足够接近了),它在速度上比第一个方法有了巨大的改进(至少在我的 Chrome OS 浏览器中,你可以在这里自己检查:http: //jsperf.com/sort-then-json-stringify-vs-smarter-stringify),当然它使两个对象字符串表示相等!
However, I was just wondering if I had missed something and there was a built-in method for something like this all along that didn't a) drive the JavaScript GC into a pathological case or b) do way too many String concatenations. I'd rather not do those.
然而,我只是想知道我是否遗漏了一些东西,并且有一个内置的方法来处理这样的事情,它没有 a) 将 JavaScript GC 驱动到一个病态的情况或 b) 进行太多的字符串连接。我宁愿不做这些。
采纳答案by jfriend00
You can collect the keys into an array with Object.keys()
, sort that array and then checksum the keys/values in that known, predictable order. I don't know of any way to use JSON.stringify()
with all the sorted keys at once though so you'd have to do your own checksum.
您可以使用 将键收集到一个数组中Object.keys()
,对该数组进行排序,然后以已知的、可预测的顺序对键/值进行校验和。我不知道有什么方法可以同时使用JSON.stringify()
所有已排序的键,因此您必须自己进行校验和。
I am not aware of any built-in method for something like this. Object keys are NOT guaranteed to be in any particular order so it would not be safe to rely on that.
我不知道此类事情的任何内置方法。对象键不能保证按任何特定顺序排列,因此依赖它是不安全的。
If you don't have nested objects or arrays as property values, then you could do something like this:
如果您没有嵌套对象或数组作为属性值,那么您可以执行以下操作:
// 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);
}
If you want faster performance, you don't have to stringify (that was just done here to save code). You could just return the flattened output
array and compare the arrays directly.
如果您想要更快的性能,则不必进行字符串化(此处只是为了保存代码而进行的)。您可以只返回扁平output
数组并直接比较数组。
If you could have embedded objects as property values, then some more work has to do to recusively expand those into the same flattened array of properties/values.
如果您可以将嵌入的对象作为属性值,那么还需要做更多的工作来将这些对象递归地扩展为相同的扁平化的属性/值数组。
回答by Fosco
You could also make a function which compares your objects in place:
您还可以创建一个函数来比较您的对象:
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;
}
This assumes they are objects passed in, and that they're simple flat objects. Logic could be added to check these assumptions and recursively check if sub-objects are equal.
这假设它们是传入的对象,并且它们是简单的平面对象。可以添加逻辑来检查这些假设并递归检查子对象是否相等。
回答by Yan Foto
3 years later...
3年后...
I came across this question as I wanted to hash my JSON objects to create Etag
s for my HTTP responses. So I ended up writing my own solution for Node, jsum
, which boils down to a simple serializer:
我遇到了这个问题,因为我想散列我的 JSON 对象来Etag
为我的 HTTP 响应创建s。所以我最终为 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
}
You can then take the result and hash it using common algorithms (e.g. SHA256
) or use the convenient method of digest
from jsum
package.
然后,您可以使用常用算法(例如SHA256
)获取结果并对其进行散列,或使用digest
from jsum
package的便捷方法。
Please note the license here!
请注意这里的许可证!