js-utl
Version:
A collection of JS utility functions to be used across several applications or libraries.
155 lines (139 loc) • 5.39 kB
JavaScript
/*
* Copyright (c) 2022 Anton Bagdatyev (Tonix)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Hashing-related utility functions.
*/
/**
* Computes a hash of an array of strings (the order of strings does not matter).
*
* NOTE: An array with duplicate values given as parameter to this function
* may yield to a hash which would collide with other hashes computed
* on different arrays with this same function.
* Therefore is on behalf of the caller to be sure that "array" contains
* unique strings.
*
* @param {string[]} array An array of strings.
* @return {number} A number representing the hash code of the array.
*/
export function stringHashArray(array) {
let code = 0;
for (let i = 0; i < array.length; i++) {
let n = 0;
for (let j = 0; j < array[i].length; j++) {
n = n * 251 ^ array[i].charCodeAt(j);
}
code ^= n;
}
return code;
}
/**
* Returns the hash of a string.
*
* @see https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript#answer-7616484
*
* @param {string} str The string.
* @return {number} The hash code of the string, represented as a number.
*/
export const hashString = str => {
let hash = 0,
i,
chr;
if (str.length === 0) {
return hash;
}
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
hash = (hash << 5) - hash + chr;
hash |= 0; // Convert to 32 bit integer.
}
return hash;
};
/**
* A one-pass algorithm to compute the hash of a series of unique strings incrementally.
*
* NOTE: Duplicate values given as parameter to this function
* may yield to a hash which would collide with other hashes computed
* on different string series with this same function.
* Therefore is on behalf of the caller to be sure that the series of strings will be unique
* while calling this function incrementally.
*
* @param {string} str The string.
* @param {number} [hash] The previous hash computed with this same function if this call is the continuation
* of the unique string series.
* @return {number} The next hash.
*/
export function onePassStringHash(str, hash = 0) {
let nextHash = hash;
let n = 0;
for (let j = 0; j < str.length; j++) {
n = n * 251 ^ str.charCodeAt(j);
}
nextHash ^= n;
return nextHash;
}
/**
* Given a sequence of integers, computes the hash of their sum so that two different sequences
* will have the same hash if the sum of their integers will be the same (the overall sum of the sequences may be higher than `Number.MAX_SAFE_INTEGER`).
*
* @see https://stackoverflow.com/questions/69749629/hash-function-that-returns-the-same-hash-for-a-sum-even-if-different-terms-lead/69749873
*
* Example:
*
* ```
* intSumHash([1, 2, 3, 4, 5, 6]); // 21
* intSumHash([1, 2, 3, 4, 5, 6]) === intSumHash([2, 3, 1, 4, 6, 5]); // true
* intSumHash([1, 2, 3, 4, 5, 6]) === intSumHash([5, 1, 10, 4, 1]); // true
* intSumHash([1, 2, 3, 4, 5, 6]) === intSumHash([21]); // true
* intSumHash([1, 2, 3, 4, 5, 6]) === intSumHash([7, 7, 7]); // true
*
* intSumHash([1, 2]); // 3
*
* const LARGE_PRIME = 999999999989;
* intSumHash([4504 * LARGE_PRIME + 1, 4504 * LARGE_PRIME + 2]); // 800745160017
*
* intSumHash([Number.MAX_SAFE_INTEGER - 1, Number.MAX_SAFE_INTEGER - 2, Number.MAX_SAFE_INTEGER - 90, 10, 13]); // 209
* intSumHash([Number.MAX_SAFE_INTEGER - 2, Number.MAX_SAFE_INTEGER - 90, 10, Number.MAX_SAFE_INTEGER - 1, 13]); // 209
* intSumHash([209, -10, 2, 3, 5]); // 209
* intSumHash([1, 2, 3, 20, 209 - (20 + 3 + 2 + 1)]); // 209
* intSumHash([4, 1, 2, 1, 1, 1, 20, 209 - 10, -86, 70, -25, 1, 7, 13]); // 209
*
* intSumHash([9007597764421056, -10, 2, 3, 5]); // 398509680158
* intSumHash([1, 2, 3, 20, 9007597764421056 - (20 + 3 + 2 + 1)]); // 398509680158
* intSumHash([4, 1, 2, 1, 1, 1, 20, 9007597764421056 - 10, -86, 70, -25, 1, 7, 13]); // 398509680158
* ```
*
* @param {number[]} seq A sequence of integers (an array).
* @return {number} The hash of the sum of the sequence of integers.
*/
export function intSumHash(seq) {
const BIG_PRIME = 4503599627370449;
let h = 0;
for (let i = 0; i < seq.length; i++) {
const value = seq[i];
h = h % BIG_PRIME + value % BIG_PRIME;
}
return h;
}
//# sourceMappingURL=hash.js.map