只是想知道是否还有其他方法。
var hashStringArray = function(array) {
array.sort();
return array.join('|');
};
我不喜欢排序太多,如果它包含在其中一个字符串中,那么使用该分隔符也不安全。总的来说,无论字符串的顺序如何,我都需要产生相同的哈希值。它将是相当短的数组(最多10个项目),但它将经常被要求所以它不应该太慢。
我打算将它与ES6 Map对象一起使用,我需要轻松找到相同的数组集合。
var theMap = new Map();
var lookup = function(arr) {
var item = null;
var hashed = hashStringArray(arr);
if (item = theMap.get( hashed )) {
return item;
}
theMap.set( hashed, itemBasedOnInput );
return itemBasedOnInput;
}
var arr1 = ['alpha','beta','gama'];
var arr2 = ['beta','alpha','gama'];
lookup(arr1) === lookup(arr2)
答案 0 :(得分:1)
作为解决方案的基础,我发现了两件事:
求和并不取决于顺序,这实际上是简单校验和中的一个缺陷(它们不能捕捉到单词中块顺序的变化),
我们可以使用他们的字符代码将字符串转换为可汇总数字
这是一个可以做(2)的功能:
charsum = function(s) {
var i, sum = 0;
for (i = 0; i < s.length; i++) {
sum += (s.charCodeAt(i) * (i+1));
}
return sum
}
这是(1)的一个版本,它通过对charsum值求和来计算数组哈希:
array_hash = function(a) {
var i, sum = 0
for (i = 0; i < a.length; i++) {
var cs = charsum(a[i])
sum = sum + (65027 / cs)
}
return ("" + sum).slice(0,16)
}
在这里小提琴:http://jsfiddle.net/WS9dC/11/
如果我们对charsum值进行了直接求和,那么数组[&#34; a&#34;,&#34; d&#34;]将具有与数组相同的哈希值[&#34; b& #34;,&#34; c&#34;] - 导致不希望的碰撞。所以基于使用非UTF字符串,其中charcodes最多为255,并且在每个字符串中允许255个字符,那么charsum的最大返回值是255 * 255 = 65025.所以我选择了下一个素数,65027,并使用(65027 / cs)来计算哈希值。我并不是100%确信这可以消除碰撞......或许需要更多的思考......但它肯定会修复[a,d]与[b,c]的情况。 测试:
var arr1 = ['alpha','beta','gama'];
var arr2 = ['beta','alpha','gama'];
console.log(array_hash(arr1))
console.log(array_hash(arr2))
console.log(array_hash(arr1) == array_hash(arr2))
输出:
443.5322979371356
443.5322979371356
true
测试一个显示不同哈希值的案例:
var arr3 = ['a', 'd'];
var arr4 = ['b', 'c'];
console.log(array_hash(arr3))
console.log(array_hash(arr4))
console.log(array_hash(arr3) == array_hash(arr4))
输出:
1320.651443298969
1320.3792001649144
false
修改强>
这是一个修订版本,它会忽略数组中的重复项,并仅根据唯一项返回哈希:
array_hash = function(a) {
var i, sum = 0, product = 1
for (i = 0; i < a.length; i++) {
var cs = charsum(a[i])
if (product % cs > 0) {
product = product * cs
sum = sum + (65027 / cs)
}
}
return ("" + sum).slice(0, 16)
}
测试:
var arr1 = ['alpha', 'beta', 'gama', 'delta', 'theta', 'alpha', 'gama'];
var arr2 = ["beta", "gama", "alpha", "theta", "delta", "beta"];
console.log(array_hash(arr1))
console.log(array_hash(arr2))
console.log(array_hash(arr1) === array_hash(arr2))
返回:
689.878503111701
689.878503111701
true
修改强>
我修改了上面的答案,以解释具有相同字母的单词数组。我们需要这些来返回不同的哈希值,他们现在这样做:
var arr1 = ['alpha', 'beta']
var arr2 = ['alhpa', 'ateb']
修复是基于char索引为charsum func添加一个乘数:
sum += (s.charCodeAt(i) * (i+1));
答案 1 :(得分:0)
你可以这样做:
var hashStringArray = function(array) {
return array.sort().join('\u200b');
};
\u200b
字符是一个unicode字符,也意味着null
,但与使用最广泛的\0
字符不同。
'\u200b' == '\0'
> false
答案 2 :(得分:0)
如果您为每个字符串计算数字哈希码,那么您可以将它们与顺序无关紧要的运算符组合,例如^
XOR运算符,那么您不需要对数组进行排序:
function hashStringArray(array) {
var code = 0;
for (var i = 0; i < array.length; i++) {
var n = 0;
for (var j = 0; j < array[i].length; j++) {
n = n * 251 ^ array[i].charCodeAt(j);
}
code ^= n;
}
return code
};
答案 3 :(得分:0)
如果你的可能字符串长度少于32个项目,那么拥有非常快速哈希的想法:使用内置哈希函数对字符串进行哈希处理,该函数将返回2的幂作为哈希:
function getStringHash(aString) {
var currentPO2 = 0;
var hashSet = [];
getStringHash = function ( aString) {
var aHash = hashSet[aString];
if (aHash) return aHash;
aHash = 1 << currentPO2++;
hashSet[aString] = aHash;
return aHash;
}
return getStringHash(aString);
}
然后在字符串数组上使用此哈希,或者哈希(|):
function getStringArrayHash( aStringArray) {
var aHash = 0;
for (var i=0; i<aStringArray.length; i++) {
aHash |= getStringHash(aStringArray[i]);
}
return aHash;
}
所以要测试一下:
console.log(getStringHash('alpha')); // 1
console.log(getStringHash('beta')); // 2
console.log(getStringHash('gamma')); // 4
console.log(getStringHash('alpha')); // 1 again
var arr1 = ['alpha','beta','gama'];
var arr2 = ['beta','alpha','gama'];
var arr3 = ['alpha', 'teta'];
console.log(getStringArrayHash(arr1)); // 11
console.log(getStringArrayHash(arr2)); // 11 also, like for arr1
var arr3 = ['alpha', 'teta'];
console.log(getStringArrayHash(arr3)); // 17 : a different array has != hashset
jsbin在这里:http://jsbin.com/rozanufa/1/edit?js,console
RQ !!!使用此方法,数组被视为set,这意味着重复的项不会更改数组的哈希值!!!
这个HAS要快,因为它只使用1)函数调用2)查找3)整数运算。 所以没有排序,没有(长)字符串,没有连续。
jsperf证实: http://jsperf.com/hashing-array-of-strings/4
编辑:
带有素数的版本,在这里:http://jsbin.com/rozanufa/3/edit?js,console
// return the unique prime associated with the string.
function getPrimeStringHash(aString) {
var hashSet = [];
var currentPrimeIndex = 0;
var primes = [ 2, 3, 5, 7, 11, 13, 17 ];
getPrimeStringHash = function ( aString) {
var aPrime = hashSet[aString];
if (aPrime) return aPrime;
if (currentPrimeIndex == primes.length) aPrime = getNextPrime();
else aPrime = primes[currentPrimeIndex];
currentPrimeIndex++
hashSet[aString] = aPrime;
return aPrime;
};
return getPrimeStringHash(aString);
// compute next prime number, store it and returns it.
function getNextPrime() {
var pr = primes[primes.length-1];
do {
pr+=2;
var divides = false;
// discard the number if it divides by one earlier prime.
for (var i=0; i<primes.length; i++) {
if ( ( pr % primes[i] ) == 0 ) {
divides = true;
break;
}
}
} while (divides == true)
primes.push(pr);
return pr;
}
}
function getStringPrimeArrayHash( aStringArray) {
var primeMul = 1;
for (var i=0; i<aStringArray.length; i++) {
primeMul *= getPrimeStringHash(aStringArray[i]);
}
return primeMul;
}
function compareByPrimeHash( aStringArray, anotherStringArray) {
var mul1 = getStringPrimeArrayHash ( aStringArray ) ;
var mul2 = getStringPrimeArrayHash ( anotherStringArray ) ;
return ( mul1 > mul2 ) ?
! ( mul1 % mul2 )
: ! ( mul2 % mul1 );
// Rq : just test for mul1 == mul2 if you are sure there's no duplicates
}
测试:
console.log(getPrimeStringHash('alpha')); // 2
console.log(getPrimeStringHash('beta')); // 3
console.log(getPrimeStringHash('gamma')); // 5
console.log(getPrimeStringHash('alpha')); // 2 again
console.log(getPrimeStringHash('a1')); // 7
console.log(getPrimeStringHash('a2')); // 11
var arr1 = ['alpha','beta','gamma'];
var arr2 = ['beta','alpha','gamma'];
var arr3 = ['alpha', 'teta'];
var arr4 = ['alpha','beta','gamma', 'alpha']; // == arr1 + duplicate 'alpha'
console.log(getStringPrimeArrayHash(arr1)); // 30
console.log(getStringPrimeArrayHash(arr2)); // 30 also, like for arr1
var arr3 = ['alpha', 'teta'];
console.log(getStringPrimeArrayHash(arr3)); // 26 : a different array has != hashset
console.log(compareByPrimeHash(arr1, arr2) ); // true
console.log(compareByPrimeHash(arr1, arr3) ); // false
console.log(compareByPrimeHash(arr1, arr4) ); // true despite duplicate