Skip to content

Instantly share code, notes, and snippets.

@annibal
Last active August 24, 2023 16:08
Show Gist options
  • Save annibal/5d896cb9a8b708349303baf6de2b9886 to your computer and use it in GitHub Desktop.
Save annibal/5d896cb9a8b708349303baf6de2b9886 to your computer and use it in GitHub Desktop.
/**
* Create a function that transforms an index into an pseudo-random value, where the values are tied to a string
* @param salt string, must have at least one [a-z0-9] character
* @param parseVal [optional] function(val: number, i: number, saltRange: [number, number]) to transform the salt result
* @returns function(idx: number, skipParse: boolean = false), will always return the same value for the specific SALT and IDX
*/
const getSaltedRandomFn = (salt, parseVal) => {
const safeSalt = (salt || "").replace(/[^a-z0-9]/gi, "").toLowerCase();
const l = safeSalt.length;
if (safeSalt.length === 0) {
throw new SyntaxError(`Salt "${salt}" yielded an empty salt string`);
}
let resalt = 1;
const bitsLen = Math.min(l, 8) + 2;
function getSaltBits(i) {
const l = safeSalt.length;
const bits = Array(bitsLen)
.fill(null)
.map((_, p) => (Math.floor(((i + resalt) * 303313) / l ** p) * 1) % l);
return bits;
}
const saltChars = Array.from(new Set(safeSalt.split(""))).sort();
const maxChar = saltChars.slice(-1)[0];
const minChar = saltChars[0];
const maxStr = Array(bitsLen).fill(maxChar).join("");
const minStr = Array(bitsLen).fill(minChar).join("");
const saltRange = [parseInt(minStr, 36), parseInt(maxStr, 36)];
function getSaltedRandom(i, skipParse = false) {
const bits = getSaltBits(i);
const part = bits.map((x) => safeSalt[x]).join("");
let val = parseInt(part, 36);
if (!skipParse && typeof parseVal === 'function') {
val = parseVal(val, i, saltRange);
}
return val;
}
return getSaltedRandom;
};
// =============
// Example Usage
// =============
function main() {
// :: helper
const scale = (from, to) => {
const _scale = (value) => {
return ((value - from[0]) * (to[1] - to[0])) / (from[1] - from[0]) + to[0];
};
return _scale;
};
const parseSaltValue = (saltVal, i, saltRange) => {
const saltScale = scale(saltRange, [0, 100])
const val = saltScale(saltVal);
return Number(val.toFixed(2));
}
// :: example parameters to use as salt
const someObj = { id: 'clksm617605rfpt270sx34eri' };
const selectedDate = new Date();
const someParameter = 'development';
// Since these strings are concatenated into a salt string,
// the getSaltVal(i) will always return the same value
// for the specific i, but for each different i it will
// generate seemingly random numbers. Then parseSaltValue()
// scales the generated number into a [0, 100] range and
// limits the result to 2 decimals.
const strSalt = [
selectedDate.toJSON(),
someObj.id,
someParameter,
].join('');
// :: usage
const getSaltVal = getSaltedRandomFn(strSalt, parseSaltValue);
const from = +new Date(selectedDate.toJSON().split('T')[0] + 'T12:00:00Z');
const to = +new Date(selectedDate.toJSON().split('T')[0] + 'T15:00:00Z');
const bin = 20 * 60 * 1000;
let i = 0;
const r = [];
for (let binDt = from; binDt < to; binDt += bin) {
r.push({
time: new Date(binDt),
label: new Date(binDt).toJSON().slice(11,16),
value: getSaltVal(i++)
});
}
return r;
}
console.log(main())
// result = [
// { label: "12:00", value: 50.11, time: Date },
// { label: "12:20", value: 42.07, time: Date },
// { label: "12:40", value: 34.34, time: Date },
// { label: "13:00", value: 11.13, time: Date },
// { label: "13:20", value: 72.15, time: Date },
// { label: "13:40", value: 93.95, time: Date },
// { label: "14:00", value: 21.1, time: Date },
// { label: "14:20", value: 5.07, time: Date },
// { label: "14:40", value: 7.31, time: Date }
// ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment