I'm kind of new to JavaSCript and I want to ask wow can I make a random anagram generator, without a list of all possible anagrams (because that's not optimized)
Example:
input: "abc"
output: get the input, split it (["a", "b", "c"]), randomize the split and join them (["cba"])
This is for a command for my bot in Discord.
I've searched about this, but I just found generators for all anagrams possible in the same string.
Thank you for reading this, have a nice day
Well, you've explained it yourself. You just had to search for the commands online...
const input = 'abc';
const output = input
.split('') // make it into strings
.sort(() => 0.5 - Math.random()) // shuffle
.join(''); // convert to a string
Not sure if you need the result to be an array of a single item, or just a string :|
Also, keep in mind the output might be the same as the input! If you don't want this - call the function as many times as needed in order to get a different output.
Edit: as #PA mentioned, using the above-mentioned way to sort arrays is not a great idea. Even though it might currently work, it can lead to bad results. The reason is that the sort algorithm might (not sure) need static values for each item. Theoretically, that might be a problem. Therefore something like this can be used:
const input = 'abc';
const output = input
.split('') // make it into strings
.map((v) => [Math.random(), v]) // convert the array of characters into tuples (pairs)
// of a random element and the real value
.sort((a, b) => a[0] - b[0]) // sort, based on the random values
.map(([rand, value]) => value) // return to list of characters
.join(''); // convert to a single string
This way you set the random value once, then do the shuffling, then remove the random values.
Related
I am trying to create a FEN notation converter for chess. I've decided the best way to go about this is to remove any unnecessary values from the string that I don't need. Here is what it looks like.
rnbqkbnr/pppppppp/8/
These are all strings in my code. I've already found solutions for the end of each file which is represented by the / in the string. But my new problem is taking away the number values and adding back in blank squares to cover them.
When you see a number in FEN, that is essentially how many empty squares on the Chess table there are until another piece. My idea once again, to dumb it down for myself, was to convert these numbers like 8 into this 11111111 a series of numbers representing each square that would have to be empty.
While in practice, I assumed that I would just be able to splice the 8 out and just start filling up that index onwards with 1, this seems to be viable option, but unfortunately is quite buggy when moving the number around to different places.
Would anyone have a better option for this? Yes I know that there is already libraries that accomplish this, but what's the fun of coding if you don't attempt to reinvent the wheel a couple times.
correctFen function - maps over the string looking for any numbers
const correctFen = () => {
newFen.map((pos, index) => {
if (Number(pos) && pos !== '/'){
for (let i = 0; i < Number(pos); i++) {
console.log('firing')
setNewFen(addAfter(newFen, index + i, '1'))
}
}
})
console.log(newFen)
figureOutPos()
}
after looking at this, it's not really removing the index that I'm wanting, could be a problem here, it's adding the 1s after the last /
function addAfter(array, index, newItem) {
return [
...array.slice(0, index),
newItem,
...array.slice(index)
];
}
Looks like you're modifying newFen from the map callback. This would indeed mess up indices because everything after the current index shifts around.
Instead, return the new character(s) from the map callback. In most cases this will be the original character, but if it's a digit, you'll return a string of multiple characters. Then join the array together into a string again.
Something like this:
// Assuming newFen is a string in some outer scope.
const correctFen = () => {
const correctedFen = newFen
// Split the string into an array of single characters.
.split('')
// Apply a function to each character and gather the results in an array.
.map((char, _index) => {
// Try to parse `char` as an integer.
const number = parseInt(char)
if (isNaN(number)) {
// `char` is not a digit. Return the character itself.
return char
} else {
// `char` is a digit. Return a string of that many 1s.
return '1'.repeat(number)
}
})
// Join the array back into a string.
.join('')
setNewFen(correctedFen)
}
This question already has answers here:
Issue with combining large array of numbers into one single number
(3 answers)
Closed 1 year ago.
I have the following array and I want to join it into a number
const arr = [6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,3]
const digits = arr.join("") //6145390195186705543
const digitsToNumber = +arr.join("") //6145390195186705000
console.log(digits);
console.log(digitsToNumber);
You can see that the join function works. However, when I try to convert it into a number, it shows a weird value. Do you guys know why it happened that way?
As stated in the comments, the value is too large for JavaScript and is truncated.
We can use BigInt to prevent this. Use with caution!
const arr = [6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,3]
const digits = arr.join('');
const digitsToNumber = +arr.join("");
const bigDigitsToNumber = BigInt(arr.join(''));
console.log(digits); // 6145390195186705543
console.log(digitsToNumber); // 6145390195186705000
console.log(bigDigitsToNumber.toString()); // 6145390195186705543
They will log different results because you are exceeding Number.MAX_SAFE_INTEGER - the highest value JS can safely compare and represent numbers.
One method to check if you are ever exceeding the limit (besides remembering the value) is Number.isSafeInteger()
Number.isSafeInteger(digitsToNumber); //false
From the docs: For larger integers, consider using the BigInt type.
To convert your concatenated string into a number you could use parseInt("123") method.
const number= parseInt(digitsToNumber)
However because of your number is too big or could be bigger, Javascript can not handle that numbers which contains more than 16 digit. If you also have a problem like that you can use some external big number libraries like BigNumber.Js.
Edit: According to Teemu's comment, you could also use link native big integer handling library.
I am working on a project where I need to covert a list of words into all possible options using symbols / numbers.
I actually want functionality pretty much identical to This Stack Overflow Question, however, it is written in Python using the intertools.product method. I do not know how to convert this into JS.
For example:
let givenInput= 'hello';
let expectedOutput = ["hello", "h3llo", "he1lo", "hel1o", "hell0", "h31lo", "h3l1o", "h3ll0", "he11o", "he1l0", "hel10", "h311o", "h31l0", "h3l10", "he110", "h3110"];
I tried having an object with the replacements I am expecting:
let REPLACE = {
'o': '0',
'e': '3',
'l': '1',
'a': '#'
}
I have tried a more manual method, where I replace one letter, add that option to an array, change another, add that to the array, etc. But I need to know too much about the string I am manipulating.
I have also tried to just use string.replace() using a regex, but that only really seems to work when I am replacing ALL of one character with another one and not one at a time.
The end goal of this is a generator to generate a list of words I do not want to be allowed in a name creator. So like, I want to exclude bad words, and all possible letter-replacement versions of those words.
You could build an array of replacement characters and get the cartesian product.
const
input= 'hello',
REPLACE = { o: '0', e: '3', l: '1', a: '#' },
result = Array
.from(input, c => c in REPLACE ? [c, REPLACE[c]] : [c])
.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []))
.map(a => a.join(''));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Imagine this approach:
Take the first letter of the input. Either it can or can't be substituted. This creates a list of options for the first letter which contains either one or two elements (either the letter alone, or the letter along with its substitution)
Take all the letters beyond the first letter, and get their permutations (in other words, solve this problem for all letters beyond the 1st)
If the options for the first letter includes just one option, simply prepend that character to each result in the list of permutations for the letters beyond the first (and you're done!) If there are two options for the first letter return the list of beyond-the-first-letter-permutations doubled; but for every item in the first half of that list prepend the first initial-letter-option, and for every item in the second half prepend the second initial-letter option.
It may seem magical that this solution requires itself to already be defined (in step #2), but this is no problem for many programming languages.
Here we'll define permuteSubstitutions(str, subs) as the function which solves this problem. Now as we're implementing the full solution we can gloss over step #2 by simply referring to permuteSubtitutions.
let permuteSubstitutions = function*(str, subs) {
// There's only one possible permutation for the empty string
if (!str) { yield ''; return; }
// Either 1 or 2 options for the head:
// - Only the leading character, if it can't be substituted,
// - or the leading character and substituted character
let heads = subs.hasOwnProperty(str[0])
? [ str[0], subs[str[0]] ]
: [ str[0] ];
// `str`, minus its first letter
let tail = str.slice(1);
// For each possible leading letter yield a result for each permutation
// of the tailing letters. This works because of the definition we gave
// to `permuteSubstitutions`! We'll get every permutation of the `tail`,
// and combine it with the possibilities for `head`.
for (let permutedTail of permuteSubstitutions(tail, subs)) {
for (let head of heads) {
yield head + permutedTail;
}
}
};
let results = permuteSubstitutions('hello', {
'o': '0',
'e': '3',
'l': '1',
'a': '#'
});
for (let result of results) console.log(result);
You may have noticed that every substitutable letter doubles the number of resulting permutations. For that reason I've implemented this for you as a function*(...){...}, which will have no issue returning a vast number of results (unlike a buffered/Array-based approach). You should still be careful with this code, as if someone manages to get your code to verify the term oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo your cpu may wind up performing 2^100 iterations, and that will make it quite sad.
Note that if you are trying to detect terms that may contain homoglyphs you will have much more success "normalizing" words and directly comparing them against your dictionary. Just replace all "homoglyphic" letters with their base meaning (e.g. transform 4 -> A, # -> A, 1 -> L, 3 -> E, etc.) This would transform a string like:
1#NGU4G3
into:
LANGUAGE
allowing the word to be directly compared to a dictionary. This overall will have a much more reasonable runtime.
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);
This seems to work, on an array of strings that look like numbers (they're numbers from a CSV file read in with csv-parse, which seems to convert everything into strings):
var a = ['123.1', '1234.0', '97.43', '5678'];
Math.max.apply(Math, a);
Returns 5678.
Does Math.max convert strings to numbers automatically?
Or should I do a + conversion myself first to be extra safe?
Does Math.max convert strings to numbers automatically?
Quoting the ECMA Script 5.1 Specification for Math.max,
Given zero or more arguments, calls ToNumber on each of the arguments and returns the largest of the resulting values.
So, internally all the values are tried to convert to a number before finding the max value and you don't have to explicitly convert the strings to numbers.
But watch out for the NaN results if the string is not a valid number. For example, if the array had one invalid string like this
var a = ['123.1', '1234.0', '97.43', '5678', 'thefourtheye'];
console.log(Math.max.apply(Math, a));
// NaN
You'll get a NaN if any of the strings aren't numbers, but otherwise it should work fine. I'd add the + just to be safe.
Consider this situation:
<script>
var a=['123.1', '1234.0', '97.43', '5678','0 11111111'];
console.log(Math.max.apply(Math, a));
</script>
You need to cast elements from array to be extra safe..
if you intend to check for the max element in an array of strings using Math.max() method. you can compare the length of reach element
question: var a = ['123.1', '1234.0', '97.43', '5678'];
const checkLength = Math.max.apply(null, a.map(element => element.length));
or using spread operator for shorter form
const checkLength = Math.max(...a.map(element => element.length));
then filter to get all the elements
a.filter(elem => elem.length === checkLength)