feat(utils): stringToIntegerInRange

This commit is contained in:
Carlos Garcia Ortiz karliatto
2025-06-09 12:13:07 +02:00
committed by karliatto
parent d51c2b94f3
commit 6d9116d6bd
3 changed files with 60 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
/**
*
* @param input - any string
* @param max - max number not included for the range, if 100 is provided the range will be 0 to 99, if 101, then range is 0 to 100
* @returns number
* @see https://stackoverflow.com/questions/6122571/simple-non-secure-hash-function-for-javascript
*/
export const getIntegerInRangeFromString = (input: string, max: number): number => {
let hash = 0;
if (input.length === 0) {
return 0;
}
// Non secure but fash hash function with browser compatibility
// https://stackoverflow.com/questions/6122571/simple-non-secure-hash-function-for-javascript
for (let i = 0; i < input.length; i++) {
const char = input.charCodeAt(i);
hash = (hash << 5) - hash + char;
// Convert to 32bit integer.
hash |= 0;
}
// The result of `x % n` is always in the range `-(n-1)` to `n-1`.
// Taking the absolute value ensures it's in the range 0 to max.
return Math.abs(hash % max);
};

View File

@@ -58,3 +58,4 @@ export * from './typedObjectKeys';
export * from './urlToOnion';
export * from './zip';
export * from './removeTrailingSlashes';
export * from './getIntegerInRangeFromString';

View File

@@ -0,0 +1,33 @@
import { getIntegerInRangeFromString } from '../src/getIntegerInRangeFromString';
describe('getIntegerInRangeFromString', () => {
it('should always produce the same number for the same string input', () => {
const input = '47A2176AC04E609FD0E9811A';
const result1 = getIntegerInRangeFromString(input, 101);
const result2 = getIntegerInRangeFromString(input, 101);
expect(result1).toEqual(result2);
});
it('should always be deterministic in producing a number from same input', () => {
const input = 'z';
const result1 = getIntegerInRangeFromString(input, 101);
expect(result1).toBe(21);
});
it('should return a number between 0 and 100 for various inputs', () => {
const inputs = [
'z',
'', // It should work with empty string as well.
'a-much-longer-string-to-test-just-to-test-that-something-like-this-will-also-work',
'47A2176AC04E609FD0E9811A',
'!@#$%^&*()-and-some-other-characters-ěščřžýáíé',
'another-device-id-456',
];
inputs.forEach(input => {
const result = getIntegerInRangeFromString(input, 101);
expect(result).toBeGreaterThanOrEqual(0);
expect(result).toBeLessThan(101);
});
});
});