简体   繁体   中英

Trying to recursively hash values in object

friends. I'm trying to write code that hashes all values in a JSON file, regardless of file structure, while preserving the keys and structure. I'm new to javascript, and am having some trouble. My code hashes the values of big and baz, but doesn't recursively hash the values of cat and bar like I want it to. Ideally, I want the numbers hashed and the names (big, foo, etc.) left alone. Thank you so much: See my code below:

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 you're looking to do this recursively, I would suggest using a generic transformation function that handles the recursive object structure and delegates to a supplied function the actual work of transforming the leaf nodes.

In this version, the transform function does all the heavy lifting. It calls the supplied function on scalar values and recursively calls itself on objects and arrays, recreating the structure of the original with the new values. This is quite reusable.

We create our hashObject function by passing transform a function which does the sha256 encoding of our values.

 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>

Scott's answer is wonderful. The optional chaining operator , ?. , is supported most places now and is particularly useful for runtime type checking. I'm sharing this post as a way to see transform expressed using this modern feature -

 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"
      ]
    }
  }
}

One distinct advantage to this approach is the Array and Object branches can appear in any order. When using Array.isArray(t) it must be checked before checking Object(t) === t . It's a subtle thing but is worth noting -

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

You may also wish to hash an entire object. Here's one possibility to implement a generic hash using a generic traverse function -

 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

Everything is ok but the parenthesis:

    if(typeof(obj[vaz] != "object"))

should read:

    if(typeof(obj[vaz]) != "object")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM