尝试递归地散列对象中的值
Trying to recursively hash values in object
朋友。我正在尝试编写对 JSON 文件中的所有值进行哈希处理的代码,而不管文件结构如何,同时保留键和结构。我是 javascript 的新手,遇到了一些麻烦。我的代码散列 big 和 baz 的值,但没有像我希望的那样递归地散列 cat 和 bar 的值。理想情况下,我希望对数字进行哈希处理并保留名称(big、foo 等)。太感谢了!请参阅下面的代码:
var meow = {
big: 20,
baz: {
foo: {
cat: 3,
bar: 5
}
}
};
console.log(typeof(meow.baz.foo));
function hashobj(obj)
{
var valarray = Object.keys(obj);
var zer = valarray[0];
for(var i = 0; i < valarray.length; i++)
{
var vaz = valarray[i];
if(typeof(obj[vaz] != "object"))
{
obj[vaz] = sha256(obj[vaz] + buf);
}
if(typeof(obj[vaz]) == "object")
{
console.log("HERE");
hashobj(obj[vaz]);
}
}
}
hashobj(meow);
console.log(meow);
一切正常,但括号:
if(typeof(obj[vaz] != "object"))
应改为:
if(typeof(obj[vaz]) != "object")
如果您希望以递归方式执行此操作,我建议使用通用转换函数来处理递归对象结构并将转换叶节点的实际工作委托给提供的函数。
在此版本中,transform
函数完成了所有繁重的工作。它在标量值上调用提供的函数,并在对象和数组上递归调用自身,用新值重新创建原始结构。这是相当可重复使用的。
我们通过传递 transform
一个对我们的值进行 sha256 编码的函数来创建 hashObject
函数。
const transform = (fn) => (obj) =>
Array.isArray (obj)
? obj .map (transform (fn))
: Object (obj) === obj
? Object .fromEntries (Object .entries (obj)
.map (([k, v]) => [k, transform (fn) (v)])
)
: fn (obj)
const hashObj = transform ((n) => sha256 (String (n)))
const meow = {big: 20, baz: {foo: {cat: 3, bar: 5, qux: [1, 2, 3]}}};
// added to demonstrate arrays --------^
console .log (hashObj (meow))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://unpkg.com/js-sha256@0.9.0/src/sha256.js"></script>
斯科特的回答很精彩。现在大多数地方都支持 optional chaining operator、?.
,并且对运行时类型检查特别有用。我分享此 post 是为了查看 transform
使用此现代功能表达的方式 -
function transform (f, o)
{ switch (o?.constructor) // <- any o, even null and undefined
{ case Array:
return o.map(_ => transform(f, _))
case Object:
return Object.fromEntries
( Object
.entries(o)
.map(([k, _]) => [k, transform(f, _)])
)
default:
return f(o)
}
}
const result =
transform
( _ => sha256(String(_))
, {big: 20, baz: {foo: {cat: 3, bar: 5, qux: [1, 2, 3]}}}
)
console.log(result)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://unpkg.com/js-sha256@0.9.0/src/sha256.js"></script>
{
"big": "f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b",
"baz": {
"foo": {
"cat": "4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce",
"bar": "ef2d127de37b942baad06145e54b0c619a1f22327b2ebbcfbec78f5564afe39d",
"qux": [
"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b",
"d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35",
"4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce"
]
}
}
}
这种方法的一个明显优势是 Array
和 Object
分支可以以任何顺序出现。使用 Array.isArray(t)
时,必须在 检查 Object(t) === t
之前检查 。这是一件微妙的事情,但值得注意 -
// also correct!
function transform (f, o)
{ switch (o?.constructor)
{ case Object: // <- type-check Object
return // ...
case Array: // <- type-check Array
return // ...
default:
return f(o)
}
}
您可能还希望散列整个对象。这是使用通用 traverse
函数实现通用 hash
的一种可能性 -
function* traverse (t, r = [])
{ switch (t?.constructor) // <- any t, even null and undefined
{ case Array:
case Set:
case Map:
for (const [k, _] of t.entries())
yield* traverse(_, [...r, k])
break
case Object:
for (const [k, _] of Object.entries(t))
yield* traverse(_, [...r, k])
break
default:
yield [r, t]
}
}
function hash (t)
{ const r = sha256.create()
for (const [k, v] of traverse(t))
r.update(k.concat(v).join(":"))
return r.hex()
}
const input =
{big: 20, baz: {foo: {cat: 3, bar: 5, qux: [1, 2, 3]}}}
console.log(hash("foo"), hash("foo"))
console.log(hash([1,2,3]), hash([1,2,3]))
console.log(hash(input), hash(input))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://unpkg.com/js-sha256@0.9.0/src/sha256.js"></script>
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
492f06976c8bc705819f5d33d71be6a80a547b03f87c377e3543605d8260159c
492f06976c8bc705819f5d33d71be6a80a547b03f87c377e3543605d8260159c
d1ae8b8641d3d6d65b1e4eecab0484a9f9618f2aabafe473c8bb0b4f6382695c
d1ae8b8641d3d6d65b1e4eecab0484a9f9618f2aabafe473c8bb0b4f6382695c
朋友。我正在尝试编写对 JSON 文件中的所有值进行哈希处理的代码,而不管文件结构如何,同时保留键和结构。我是 javascript 的新手,遇到了一些麻烦。我的代码散列 big 和 baz 的值,但没有像我希望的那样递归地散列 cat 和 bar 的值。理想情况下,我希望对数字进行哈希处理并保留名称(big、foo 等)。太感谢了!请参阅下面的代码:
var meow = {
big: 20,
baz: {
foo: {
cat: 3,
bar: 5
}
}
};
console.log(typeof(meow.baz.foo));
function hashobj(obj)
{
var valarray = Object.keys(obj);
var zer = valarray[0];
for(var i = 0; i < valarray.length; i++)
{
var vaz = valarray[i];
if(typeof(obj[vaz] != "object"))
{
obj[vaz] = sha256(obj[vaz] + buf);
}
if(typeof(obj[vaz]) == "object")
{
console.log("HERE");
hashobj(obj[vaz]);
}
}
}
hashobj(meow);
console.log(meow);
一切正常,但括号:
if(typeof(obj[vaz] != "object"))
应改为:
if(typeof(obj[vaz]) != "object")
如果您希望以递归方式执行此操作,我建议使用通用转换函数来处理递归对象结构并将转换叶节点的实际工作委托给提供的函数。
在此版本中,transform
函数完成了所有繁重的工作。它在标量值上调用提供的函数,并在对象和数组上递归调用自身,用新值重新创建原始结构。这是相当可重复使用的。
我们通过传递 transform
一个对我们的值进行 sha256 编码的函数来创建 hashObject
函数。
const transform = (fn) => (obj) =>
Array.isArray (obj)
? obj .map (transform (fn))
: Object (obj) === obj
? Object .fromEntries (Object .entries (obj)
.map (([k, v]) => [k, transform (fn) (v)])
)
: fn (obj)
const hashObj = transform ((n) => sha256 (String (n)))
const meow = {big: 20, baz: {foo: {cat: 3, bar: 5, qux: [1, 2, 3]}}};
// added to demonstrate arrays --------^
console .log (hashObj (meow))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://unpkg.com/js-sha256@0.9.0/src/sha256.js"></script>
斯科特的回答很精彩。现在大多数地方都支持 optional chaining operator、?.
,并且对运行时类型检查特别有用。我分享此 post 是为了查看 transform
使用此现代功能表达的方式 -
function transform (f, o)
{ switch (o?.constructor) // <- any o, even null and undefined
{ case Array:
return o.map(_ => transform(f, _))
case Object:
return Object.fromEntries
( Object
.entries(o)
.map(([k, _]) => [k, transform(f, _)])
)
default:
return f(o)
}
}
const result =
transform
( _ => sha256(String(_))
, {big: 20, baz: {foo: {cat: 3, bar: 5, qux: [1, 2, 3]}}}
)
console.log(result)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://unpkg.com/js-sha256@0.9.0/src/sha256.js"></script>
{
"big": "f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b",
"baz": {
"foo": {
"cat": "4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce",
"bar": "ef2d127de37b942baad06145e54b0c619a1f22327b2ebbcfbec78f5564afe39d",
"qux": [
"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b",
"d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35",
"4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce"
]
}
}
}
这种方法的一个明显优势是 Array
和 Object
分支可以以任何顺序出现。使用 Array.isArray(t)
时,必须在 检查 Object(t) === t
之前检查 。这是一件微妙的事情,但值得注意 -
// also correct!
function transform (f, o)
{ switch (o?.constructor)
{ case Object: // <- type-check Object
return // ...
case Array: // <- type-check Array
return // ...
default:
return f(o)
}
}
您可能还希望散列整个对象。这是使用通用 traverse
函数实现通用 hash
的一种可能性 -
function* traverse (t, r = [])
{ switch (t?.constructor) // <- any t, even null and undefined
{ case Array:
case Set:
case Map:
for (const [k, _] of t.entries())
yield* traverse(_, [...r, k])
break
case Object:
for (const [k, _] of Object.entries(t))
yield* traverse(_, [...r, k])
break
default:
yield [r, t]
}
}
function hash (t)
{ const r = sha256.create()
for (const [k, v] of traverse(t))
r.update(k.concat(v).join(":"))
return r.hex()
}
const input =
{big: 20, baz: {foo: {cat: 3, bar: 5, qux: [1, 2, 3]}}}
console.log(hash("foo"), hash("foo"))
console.log(hash([1,2,3]), hash([1,2,3]))
console.log(hash(input), hash(input))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://unpkg.com/js-sha256@0.9.0/src/sha256.js"></script>
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
492f06976c8bc705819f5d33d71be6a80a547b03f87c377e3543605d8260159c
492f06976c8bc705819f5d33d71be6a80a547b03f87c377e3543605d8260159c
d1ae8b8641d3d6d65b1e4eecab0484a9f9618f2aabafe473c8bb0b4f6382695c
d1ae8b8641d3d6d65b1e4eecab0484a9f9618f2aabafe473c8bb0b4f6382695c