Trying to recursively hash values in object

吃可爱长大的小学妹 提交于 2021-02-05 09:26:28

问题


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);

回答1:


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>



回答2:


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



回答3:


Everything is ok but the parenthesis:

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

should read:

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


来源:https://stackoverflow.com/questions/65096877/trying-to-recursively-hash-values-in-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!