How can I create a UUID from the Timestamp number using Javascript?
Is there any existente API?
According to the Wikipedia page, a UUID is a 128-bit number. Javascript numbers are 64-bit floating point numbers (according to this SO answer), so I assume that you already have your UUID number in the form of a string.
Quoting Wikipedia: "a UUID consists of 32 hexadecimal digits, displayed in 5 groups separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters (32 digits and 4 hyphens)." Javascript Number's toString method can be given a base (hexadecimal is base 16) but of course we can't use Numbers here.
So, you'll need some sort of code that can first handle 128-bit numbers and then convert them to hexadecimal. There are various BigDecimal and BigNumber Javascript libraries knocking about. Just find one that you like, perhaps using a SO question as a guide. Having done that you'll have a string like so:
var hexNum = "550e8400e29b41d4a716446655440000";
Then you simple combine the different substrings with - separators and you have your UUID string:
var UUID = hexNum.substr(0, 8) + '-' + hexNum.substr(8, 4) + '-' +
hexNum.substr(12, 4) + '-' + hexNum.substr(16, 4) + '-' + hexNum.substr(20)
Update: In the process of writing my response the original question was updated to ask how you'd create a UUID from a 'Timestamp number'. I'm not sure what that'd be, perhaps a Unix timestamp, for instance the result of Date.now(). Since UUIDs are supposed to be (practically) unique and a millisecond time is hardly unique, I'd imagine you'd want to introduce some further element of uniqueness into the number before creating the number. Even if you didn't, you'd still need to convert 64-bit Number to a 128-bit number, again using some sort of BigDecimal or BigNumber library.
Related
I'm using a barcode scanner to read a barcode on my website (the website is made in OpenUI5).
The scanner works like a keyboard that types the characters it reads. At the end and the beginning of the typing it uses a special character. These characters are different for every type of scanner.
Some possible characters are:
█
▄
–
—
In my code I use if (oModelScanner.oData.scanning && oEvent.key == "\u2584") to check if the input from the scanner is ▄.
Is there any way to get the code from that character in the \uHHHH style? (with the HHHH being the hexadecimal code for the character)
I tried the charCodeAt but this returns the decimal code.
With the codePointAt examples they make the code I need into a decimal code so I need a reverse of this.
Javascript strings have a method codePointAt which gives you the integer representing the Unicode point value. You need to use a base 16 (hexadecimal) representation of that number if you wish to format the integer into a four hexadecimal digits sequence (as in the response of Nikolay Spasov).
var hex = "▄".codePointAt(0).toString(16);
var result = "\\u" + "0000".substring(0, 4 - hex.length) + hex;
However it would probably be easier for you to check directly if you key code point integer match the expected code point
oEvent.key.codePointAt(0) === '▄'.codePointAt(0);
Note that "symbol equality" can actually be trickier: some symbols are defined by surrogate pairs (you can see it as the combination of two halves defined as four hexadecimal digits sequence).
For this reason I would recommend to use a specialized library.
you'll find more details in the very relevant article by Mathias Bynens
var hex = "▄".charCodeAt(0).toString(16);
var result = "\\u" + "0000".substring(0, 4 - hex.length) + hex;
If you want to print the multiple code points of a character, e.g., an emoji, you can do this:
const facepalm = "🤦🏼♂️";
const codePoints = Array.from(facepalm)
.map((v) => v.codePointAt(0).toString(16))
.map((hex) => "\\u{" + hex + "}");
console.log(codePoints);
["\u{1f926}", "\u{1f3fc}", "\u{200d}", "\u{2642}", "\u{fe0f}"]
If you are wondering about the components and the length of 🤦🏼♂️, check out this article.
I ran into this issue when trying to get the last 4 digits of a 19 digits numeric value.
let payload={
card: 1234567891238475891
}
let stringCardNumber = '' + payload.card;
console.log(payload.card)
console.log(stringCardNumber)
console.log( stringCardNumber.slice(stringCardNumber.length - 4))
let zeroedCardNumber = stringCardNumber.slice(0, 6) + "".padStart(stringCardNumber.length - 10, "0") + stringCardNumber.slice(stringCardNumber.length - 4);
console.log(zeroedCardNumber)
So for 1234567891238475891, the output log is 1234567891238475800.
Changing the card value to string in the json itself is not the solution I am expecting, since there will be cases where 19 digits number is expected.
That number is too large for JavaScript's default numerical representation so you need to use the longer form with BigInt:
card: 1234567891238475891n
A better approach is to just use a string as these aren't really "numbers" in the conventional sense and as Pointy adds, support for BigInt is a relatively new thing so if support for older browsers is important it won't be a solution.
JSON does not enforce BigInt support, so numerical values this large may well get rounded. Using a string is the most reliable way to ensure this data flows through correctly.
I am wondering how to generate a GUID given an input string, such that the same input string results in the same GUID (sort of like an MD5 hash). The problem with MD5 hashes is they just guarantee low collision rate, rather than uniqueness. Instead I would like something like this:
guid('v1.0.0') == 1231231231123123123112312312311231231231
guid('v1.0.1') == 6154716581615471658161547165816154716581
guid('v1.0.2') == 1883939319188393931918839393191883939319
How would you go about implementing this sort of thing (ideally in JavaScript)? Is it even possible to do? I am not sure where to start. Things like the uuid module don't take a seed string, and they don't let you use a custom format/alphabet.
I am not looking for the canonical UUID format, but rather a GUID, ideally one made up of just integers.
What you would need is define a one-to-one mapping of text strings (such as "v1.0.0") onto 40 digit long strings (such as "123123..."). This is also known as a bijection, although in your case an injection (a simple one-to-one mapping from inputs to outputs, not necessarily onto) may be enough. As you note, hash functions don't necessarily ensure this mapping, but there are other possibilities, such as full-period linear congruential generators (if they take a seed that you can map one-to-one onto input string values), or other reversible functions.
However, if the set of possible input strings is larger than the set of possible output strings, then you can't map all input strings one-to-one with all output strings (without creating duplicates), due to the pigeonhole principle.
For example, you can't generally map all 120-character strings one-to-one with all 40-digit strings unless you restrict the format of the 120-character strings in some way. However, your problem of creating 40-digit output strings can be solved if you can accept limiting input strings to no more than 1040 values (about 132 bits), or if you can otherwise exploit redundancy in the input strings so that they are guaranteed to compress losslessly to 40 decimal digits (about 132 bits) or less, which may or may not be possible. See also this question.
The algorithm involves two steps:
First, transform the string to a BigInt by building up the string's charCodeAt() values similarly to the stringToInt method given in another answer. Throw an error if any charCodeAt() is 0x80 or greater, or if the resulting BigInt is equal to or greater than BigInt(alphabet_length)**BigInt(output_length).
Then, transform the integer to another string by taking the mod of the BigInt and the output alphabet's size and replacing each remainder with the corresponding character in the output alphabet, until the BigInt reaches 0.
One approach would be to use the method from that answer:
/*
* uuid-timestamp (emitter)
* UUID v4 based on timestamp
*
* Created by tarkh
* tarkh.com (C) 2020
* https://stackoverflow.com/a/63344366/1261825
*/
const uuidEmit = () => {
// Get now time
const n = Date.now();
// Generate random
const r = Math.random(); // <- swap this
// Stringify now time and generate additional random number
const s = String(n) + String(~~(r*9e4)+1e4);
// Form UUID and return it
return `${s.slice(0,8)}-${s.slice(8,12)}-4${s.slice(12,15)}-${[8,9,'a','b'][~~(r*3)]}${s.slice(15,18)}-${s.slice(s.length-12)}`;
};
// Generate 5 UUIDs
console.log(`${uuidEmit()}
${uuidEmit()}
${uuidEmit()}
${uuidEmit()}
${uuidEmit()}`);
And simply swap out the Math.random() call to a different random function which can take your seed value. (There are numerous algorithms out there for creating a seedable random method, so I won't try prescribing a particular one).
Most random seeds expect numeric, so you could convert a seed string to an integer by just adding up the character values (multiplying each by 10^position so you'll always get a unique number):
const stringToInt = str =>
Array.prototype.slice.call(str).reduce((result, char, index) => result += char.charCodeAt(0) * (10**(str.length - index)), 0);
console.log(stringToInt("v1.0.0"));
console.log(stringToInt("v1.0.1"));
console.log(stringToInt("v1.0.2"));
If you want to generate the same extract string every time, you can take a similar approach to tarkh's uuidEmit() method but get rid of the bits that change:
const strToInt = str =>
Array.prototype.slice.call(str).reduce((result, char, index) => result += char.charCodeAt(0) * (10**(str.length - index)), 0);
const strToId = (str, len = 40) => {
// Generate random
const r = strToInt(str);
// Multiply the number by some things to get it to the right number of digits
const rLen = `${r}`.length; // length of r as a string
// If you want to avoid any chance of collision, you can't provide too long of a string
// If a small chance of collision is okay, you can instead just truncate the string to
// your desired length
if (rLen > len) throw new Error('String too long');
// our string length is n * (r+m) + e = len, so we'll do some math to get n and m
const mMax = 9; // maximum for the exponent, too much longer and it might be represented as an exponent. If you discover "e" showing up in your string, lower this value
let m = Math.floor(Math.min(mMax, len / rLen)); // exponent
let n = Math.floor(len / (m + rLen)); // number of times we repeat r and m
let e = len - (n * (rLen + m)); // extra to pad us to the right length
return (new Array(n)).fill(0).map((_, i) => String(r * (i * 10**m))).join('')
+ String(10**e);
};
console.log(strToId("v1.0.0"));
console.log(strToId("v1.0.1"));
console.log(strToId("v1.0.2"));
console.log(strToId("v1.0.0") === strToId("v1.0.0")); // check they are the same
console.log(strToId("v1.0.0") === strToId("v1.0.1")); // check they are different
Note, this will only work with smaller strings, (probably about 10 characters top) but it should be able to avoid all collisions. You could tweak it to handle larger strings (remove the multiplying bit from stringToInt) but then you risk collisions.
I suggest using MD5...
Following the classic birthday problem, all things being equal, the odds of 2 people sharing a birthday out of a group of 23 people is ( see https://en.wikipedia.org/wiki/Birthday_problem )...
For estimating MD5 collisions, I'm going to simplify the birthday problem formula, erring in the favor of predicting a higher chance of a collision...
Note though that whereas in the birthday problem, a collision is a positive result, in the MD5 problem, a collision is a negative result, and therefore providing higher than expected collision odds provides a conservative estimate of the chance of a MD5 collision. Plus this higher predicted chance can in some way be considered a fudge factor for any uneven distribution in the MD5 output, although I do not believe there is anyway to quantify this without a God computer...
An MD5 hash is 16 bytes long, resulting in a range of 256^16 possible values. Assuming that the MD5 algorithm is generally uniform in its results, lets suppose we create one quadrillion (ie, a million billion or 10^15) unique strings to run through the hash algorithm. Then using the modified formula (to ease the collision calculations and to add a conservative fudge factor), the odds of a collision are...
So, after 10^15 or one quadrillion unique input strings, the estimated odds of a hash collision are on par with the odds of winning the Powerball or the Mega Millions Jackpot (which are on order of 1 in ~300,000,000 per https://www.engineeringbigdata.com/odds-winning-powerball-grand-prize-r/ ).
Note too that 256^16 is 340282366920938463463374607431768211456, which is 39 digits, falling within the desired range of 40 digits.
So, suggest using the MD5 hash ( converting to BigInt ), and if you do run into a collision, I will be more than glad to spot you a lottery ticket, just to have a chance to tap into your luck and split the proceeds...
( Note: I used https://keisan.casio.com/calculator for the calculations. )
While UUID v4 is just used for random ID generation, UUID v5 is more like a hash for a given input string and namespace. It's perfect for what you describe.
As you already mentioned, You can use this npm package:
npm install uuid
And it's pretty easy to use.
import {v5 as uuidv5} from 'uuid';
// use a UUIDV4 as a unique namespace for your application.
// you can generate one here: https://www.uuidgenerator.net/version4
const UUIDV5_NAMESPACE = '...';
// Finally, provide the input and namespace to get your unique id.
const uniqueId = uuidv5(input, namespace);
"" + 237498237498273908472390847239084710298374901823749081237409273492374098273904872398471298374
> '2.3749823749827392e+92'
I calculate IDs in a beautiful and arcane way:
time = new Date().getTime()
pid = process.pid
host = 0; (host +=s.charCodeAt(0) for s in os.hostname())
counter = MIPS.unique_id()
"#{host}#{pid}#{time}#{counter}"
Unfortunately, somewhere along the way the IDs (for example 11207648813339434616800). Unfortunately this means they sometimes turn to 1.1207648813339434e+22.
UPDATE:
It seems to be a "bug/feature" of redis. never expected that.
# Bug with Big Numbers on zadd
redis = require 'redis'
r = redis.createClient()
r.zadd 'zset', '342490809809999998098', 'key', ->
r.zscore 'zset', 'key', (_, results) ->
console.log typeof results # string
console.log results # 3.4249080981000002e+20
Javascript use 8-bytes double to store large numbers, which is 53bit precision. In your case, it is far beyond 53 bits, so you should use a big-number library, which can store big numbers precisely. Try javascript-bignum
Your number gets converted to 2.3749823749827392e+92 before you concatenate the number with the string to convert it.
The only solution is to use a container format that accepts an arbitrary number of digits, which is either a string or an array.
Can you provide us with a few more details as to how you are obtaining this number?
I've been writing JavaScript on and off for 13 years, but I sort of rediscovered it in the past few months as a way of writing programs that can be used by anyone visiting a web page without installing anything. See for example.
The showstopper I've recently discovered is that because JavaScript is loosely typed by design, it keeps concatenating strings when I want it to add numbers. And it's unpredictable. One routine worked fine for several days then when I fed different data into it the problem hit and I ended up with an impossibly big number.
Sometimes I've had luck preventing this by putting ( ) around one term, sometimes I've had to resort to parseInt() or parseFloat() on one term. It reminds me a little of trying to force a float result in C by putting a .00 on one (constant) term. I just had it happen when trying to += something from an array that I was already loading by doing parseFloat() on everything.
Does this only happen in addition? If I use parseInt() or parseFloat() on at least one of the terms each time I add, will that prevent it? I'm using Firefox 6 under Linux to write with, but portability across browsers is also a concern.
The specification says about the addition operator:
If Type(lprim) is String or Type(rprim) is String, then
Return the String that is the result of concatenating ToString(lprim) followed by ToString(rprim)
Which means that if at least one operator is a string, string concatenation will take place.
If I use parseInt() or parseFloat() on at least one of the terms each time I add, will that prevent it?
No, all operands have to be numbers.
You can easily convert any numerical string into a number using the unary plus operator (it won't change the value if you are already dealing with a number):
var c = +a + +b;
I normally do this:
var x = 2;
var t = "12";
var q = t+x; // q == "122"
var w = t*1+x; // *1 forces conversion to number w == 14
If t isn't a number then you'll get NaN.
If you multiply by 1 variables you don't know what type they are. They will be converted to a number. I find this method better than doing int and float casts, because *1 works with every kind of numbers.
The problem you are having is that the functions which fetch values from the DOM normally return strings. And even if it is a number it will be represented as a string when you fetch it.
You can use + operator to convert a string to number.
var x = '111'
+x === 111
Rest assured it is very predictable, you just need to be familiar with the operators and the data types of your input.
In short, evaluation is left-to-right, and concatenation will occur whenever in the presence of a string, no matter what side of the operation.
So for example:
9 + 9 // 18
9 + '9' // '99'
'9' + 9 // '99'
+ '9' + 9 // 18 - unary plus
- '9' + 9 // 0 - unary minus
Some ternary expressions:
9 + '9' + 9 // '999'
9 + 9 + '9' // '189'