【问题标题】:How to create a unique string/key form JavaScript object?如何创建唯一的字符串/键表单 JavaScript 对象?
【发布时间】:2020-09-19 00:24:11
【问题描述】:

我想从对象的值创建一个唯一的字符串/键/标识符/散列。例如:

let myObj =
{
  key1: 'val1',
  key2: 'val2',
  key3: 'val3',
  key4: 'val4'
};

function getObjectUniqueKey(obj) {
  return (`${ obj.key1 }|${ obj.key2 }|${ obj.key3 }|${ obj.key4 }`);
}

getObjectUniqueKey() 方法可以正常工作,但是它不能扩展到更大的对象(我的对象有超过 10 个键)。

在 JavaScript 中有什么方法可以创建一个唯一的字符串来代表一个对象?类似于 JSON.stringify() 的东西,但不是那么健壮吗?
JSON.stringify 也会对键进行字符串化,但我所有的对象都有相同的接口 - 相同的键名 - 所以不需要对它们进行字符串化)。

  1. { key: 'val1' }{ key: 'val2' } 应该会产生不同的结果。
  2. { key1: 'val' }{ key2: 'val' } 应该会产生不同的结果。
  3. { a: 1, b: 2 }{ b: 2, a: 1 } 应该会产生相同的结果。

【问题讨论】:

  • 你可以做JSON.stringify(myObj)并替换数字和字母以外的不必要的东西str.replace(/[\W\D]/g, '')
  • @JonasWilms - 我已经更新了我的问题,JSON.stringify 的问题在于它还会对键进行字符串化,
  • 如果只需要在ram中唯一地映射对象,最好干脆做let m = new Map(); m.set(myObj, 'someCorrespondingValue'); m.set(anotherObj, 'anotherCorrespondingValue);
  • 通过key计算对象的值对你来说重要还是唯一重要?
  • 应该{ key: 'val1' }{ key: 'val2' } 产生相同的哈希? { a: 1, b: 2 }{ b: 2, a: 1 } 是否应该产生相同的哈希? { key1: 'val' }{ key2: 'val' } 是否应该产生相同的哈希??

标签: javascript stringify


【解决方案1】:

这里有 2 个没有 JSON.stringify 的例子:

版本 1

var myObj =
{
    key1: 'val2',
    key2: 'val1',
    key4: 'val4',
    key3: 'val3',
};

/**
 * get unique string (Version 1).
 * This is fine if every object has always the same keys.
 */
function getObjectUniqueKey(obj) {
var res = '';
const keys = Object.keys(obj);
keys.sort()
    .forEach((key, i) => {
        res += obj[key]
        if (keys.length != i + 1) {
            res += '|'
        }
    });
return res;
}

console.log(`Version1: ${getObjectUniqueKey(myObj)}`);

版本2:

var myObj =
{
    key1: 'val1',
    key2: 'val2',
    key4: 'val4',
    key3: 'val3',
};

/**
 * This Version can be used when the number of keys are changing.
 * This is based on your example that
 * { key1: 'val' } and { key2: 'val' } should produce different results.
 * 
 * Here you have to use a identificator for the key names because otherwise
 * the example objects would create the same result (v1 doesn't care about this).
 */
function getObjectUniqueKeyV2(obj) {
    var res = '';
    const keys = Object.keys(obj);
    keys
        .sort()
        .forEach((key, i) => {
            res += key + ':' + obj[key]
            if (keys.length != i + 1) {
                res += '|'
            }
        });
    return res;
}


console.log(`Version2: ${getObjectUniqueKeyV2(myObj)}`);

【讨论】:

    【解决方案2】:

    如果您想生成相同的散列而不考虑键顺序,则很可能需要排序。如果您不想为大型对象使用长得离谱的键,则需要进行某种有损散列。请注意,为了完全散列一个对象,我们需要能够散列对象中的每种类型的值。下面的逻辑主要使用lossyHashStrs 散列字符串数组。它还可以通过将所有键和值收集到一个数组中来将一个对象转换为一个字符串数组。如前所述,Object 中的每个值都需要是可散列的。下面的代码处理字符串、数字和对象值。例如,如果 Object 包含一个 Array 作为属性,它将窒息(但可以扩展来处理这个!)

    总体而言,哈希将是看起来像 12 位十进制数字的字符串,必要时带有前导零。

    // A utility function for checking variable types
    let isType = (v, type) => (v == null && type == null) || v.constructor === type;
    
    let lossyHashStrs = strs => {
      
      // Convert an Array of Strings into a String that looks like a 12-digit number
      
      let max = 999999999999;
      let n1 = 129837378393;
      let n2 = 348973984723;
      for (let str of strs) { // Loop over all strings...
        for (let i = 0; i < str.length; i++) { // Loop over characters of the current string...
          // Some homebrewed hashing chaos, which factors in the current character
          n2 = (7 + n2 * n1 * str.charCodeAt(i)) % max;
          n1 = (11 + n1 * n2 + n2 * n2) % max;
        }
      }
      return `${n1}`.padStart(`${max}`.length, '0');
    };
    
    let lossyHashObj = obj => {
      
      // Convert an Object into an Array of Strings, then return the `lossyHashStrs` result
      
      let vals = [];
      let keys = Object.keys(obj).sort();
      for (let key of keys) {
        vals.push(key);
        let val = obj[key];
        
        // Ensure that `val` can be converted to a String:
        
        // If `val` is already a String, no dilemma!
        if (isType(val, String)) vals.push(val);
        
        // If `val` is a Number, simply use `toString`
        else if (isType(val, Number)) vals.push(val.toString());
        
        // If `val` is an Object recursively call `lossyHashObj` (yes - circular references will repeat infinitely)
        else if (isType(val, Object)) vals.push(lossyHashObj(val));
        
        // TODO:
        // else if (isType(val, Array)) /* ... */
        
        // Throw an Error for any other type of value
        else throw new Error(`Object contains a ${val.constructor.name}; don't know how to hash :(`);
        
      }
      return lossyHashStrs(vals);
      
    };
    
    
    let tests = [
      [ { a: 1 }, { a: 1 } ],
      [ { a: 1 }, { a: 2 } ],
      [ { a: 1, b: 1 }, { a: 1, b: 1 } ],
      [ { a: 1, b: 2 }, { a: 2, b: 1 } ],
      [ { a: 2, b: 3 }, { b: 3, c: 4 } ],
      [ { c: 3, b: 2, a: 1 }, { a: 1, b: 2, c: 3 } ]
    ]
    
    for (let [ v1, v2 ] of tests) {
      
      let hash1 = lossyHashObj(v1);
      let hash2 = lossyHashObj(v2);
      console.log([
        `Compare:`,
        `  ${JSON.stringify(v1)} (hash: ${hash1})`,
        `  ${JSON.stringify(v2)} (hash: ${hash2})`,
        `Hashes match: ${(hash1 === hash2).toString().toUpperCase()}`
      ].join('\n'));
      
    }

    【讨论】:

      【解决方案3】:

      您可以创建一个所有键的排序数组(以确保比较具有相同键的值),然后将这些键映射到相关值并在值数组上使用JSON.stringify

        JSON.stringify(
          Object.keys(obj)
          .sort()   
          .map(k => obj[k])
       )
      

      【讨论】:

        猜你喜欢
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 2015-01-23
        • 2011-09-15
        • 1970-01-01
        • 1970-01-01
        • 2014-02-27
        • 1970-01-01
        相关资源
        最近更新 更多