Convert numbers to letters beyond the 26 character alphabet - javascript

I'm creating some client side functions for a mappable spreadsheet export feature.
I'm using jQuery to manage the sort order of the columns, but each column is ordered like an Excel spreadsheet i.e. a b c d e......x y z aa ab ac ad etc etc
How can I generate a number as a letter? Should I define a fixed array of values? Or is there a dynamic way to generate this?

I think you're looking for something like this
function colName(n) {
var ordA = 'a'.charCodeAt(0);
var ordZ = 'z'.charCodeAt(0);
var len = ordZ - ordA + 1;
var s = "";
while(n >= 0) {
s = String.fromCharCode(n % len + ordA) + s;
n = Math.floor(n / len) - 1;
}
return s;
}
// Example:
for(n = 0; n < 125; n++)
document.write(n + ":" + colName(n) + "<br>");

This is a very easy way:
function numberToLetters(num) {
let letters = ''
while (num >= 0) {
letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[num % 26] + letters
num = Math.floor(num / 26) - 1
}
return letters
}

function getColumnDescription(i) {
const m = i % 26;
const c = String.fromCharCode(65 + m);
const r = i - m;
return r > 0
? `${getColumnDescription((r - 1) / 26)}${c}`
: `Column ${c}`
}
Usage:
getColumnDescription(15)
"Column P"
getColumnDescription(26)
"Column AA"
getColumnDescription(4460)
"Column FOO"

If you have your data in a two-dimensional array, e.g.
var data = [
['Day', 'score],
['Monday', 99],
];
you can map the rows/columns to spreadsheet cell numbers as follows (building on the code examples above):
function getSpreadSheetCellNumber(row, column) {
let result = '';
// Get spreadsheet column letter
let n = column;
while (n >= 0) {
result = String.fromCharCode(n % 26 + 65) + result;
n = Math.floor(n / 26) - 1;
}
// Get spreadsheet row number
result += `${row + 1}`;
return result;
};
E.g. the 'Day' value from data[0][0] would go in spreadsheet cell A1.
> getSpreadSheetCellNumber(0, 0)
> "A1"
This also works when you have 26+ columns:
> getSpreadSheetCellNumber(0, 26)
> "AA1"

You can use code like this, assuming that numbers contains the numbers of your columns. So after this code you'll get the string names for your columns:
var letters = ['a', 'b', 'c', ..., 'z'];
var numbers = [1, 2, 3, ...];
var columnNames = [];
for(var i=0;i<numbers.length;i++) {
var firstLetter = parseInt(i/letters.length) == 0 ? '' : letters[parseInt(i/letters.length)];
var secondLetter = letters[i%letters.length-1];
columnNames.push(firstLetter + secondLetter);
}

Simple recursive solution:
function numberToColumn(n) {
const res = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[n % 26];
return n >= 26 ? numberToColumn(Math.floor(n / 26) - 1) + res : res;
}

Here is an alternative approach that relies on .toString(26). It uses conversion to base-26 and then translates the characters so they are in the a..z range:
const conv = ((base, alpha) => { // Closure for preparing the function
const map = Object.fromEntries(Array.from(alpha, (c, i) => [c, alpha[i + 10]]));
return n => (n + base).toString(26).replace(/o*p/, "").replace(/./g, m => map[m]);
})(parseInt("ooooooooop0", 26), "0123456789abcdefghijklmnopqrstuvwxyz");
// Example:
for (let n = 0; n < 29; n++) console.log(n, conv(n));
console.log("...");
for (let n = 690; n < 705; n++) console.log(n, conv(n));
About the magical number
The magical value "ooooooooop0" is derived as follows:
It is a number expressed in radix 26, in the standard way, i.e. where the ten digits also play a role, and then the first letters of the alphabet.
The greatest "digit" in this radix 26 is "p" (the 16th letter of the Latin alphabet), and "o" is the second greatest.
The magical value is formed by a long enough series of the one-but-greatest digit, followed by the greatest digit and ended by a 0.
As JavaScript integer numbers max out around Number.MAX_SAFE_INTEGER (greater integers numbers would suffer from rounding errors), there is no need to have a longer series of "o" than was selected. We can see that Number.MAX_SAFE_INTEGER.toString(26) has 12 digits, so precision is ensured up to 11 digits in radix 26, meaning we need 9 "o".
This magical number ensures that if we add units to it (in radix 26), we will always have a representation which starts with a series of "o" and then a "p". That is because at some point the last digit will wrap around to 0 again, and the "p" will also wrap around to 0, bringing the preceding "o" to "p". And so we have this invariant that the number always starts with zero or more "o" and then a "p".
More generic
The above magic number could be derived via code, and we could make it more generic by providing the target alphabet. The length of that target alphabet then also directly determines the radix (i.e. the number of characters in that string).
Here is the same output generated as above, but with a more generic function:
function createConverter(targetDigits) {
const radix = targetDigits.length,
alpha = "0123456789abcdefghijklmnopqrstuvwxyz",
map = Object.fromEntries(Array.from(alpha,
(src, i) => [src, targetDigits[i]]
)),
base = parseInt((alpha[radix-1]+'0').padStart(
Number.MAX_SAFE_INTEGER.toString(radix).length - 1, alpha[radix-2]
), radix),
trimmer = RegExp("^" + alpha[radix-2] + "*" + alpha[radix-1]);
return n => (n + base).toString(radix)
.replace(trimmer, "")
.replace(/./g, m => map[m]);
}
// Example:
const conv = createConverter("abcdefghijklmnopqrstuvwxyz");
for (let n = 0; n < 29; n++) console.log(n, conv(n));
console.log("...");
for (let n = 690; n < 705; n++) console.log(n, conv(n));
This can now easily be adapted to use a more reduced target alphabet (like without the letters "l" and "o"), giving a radix of 24 instead of 26:
function createConverter(targetDigits) {
const radix = targetDigits.length,
alpha = "0123456789abcdefghijklmnopqrstuvwxyz",
map = Object.fromEntries(Array.from(alpha,
(src, i) => [src, targetDigits[i]]
)),
base = parseInt((alpha[radix-1]+'0').padStart(
Number.MAX_SAFE_INTEGER.toString(radix).length - 1, alpha[radix-2]
), radix),
trimmer = RegExp("^" + alpha[radix-2] + "*" + alpha[radix-1]);
return n => (n + base).toString(radix)
.replace(trimmer, "")
.replace(/./g, m => map[m]);
}
// Example without "l" and "o" in target alphabet:
const conv = createConverter("abcdefghijkmnpqrstuvwxyz");
for (let n = 0; n < 29; n++) console.log(n, conv(n));
console.log("...");
for (let n = 690; n < 705; n++) console.log(n, conv(n));

This covers the range from 1 to 1000. Beyond that I haven't checked.
function colToletters(num) {
let a = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (num < 27) return a[num % a.length];
if (num > 26) {
num--;
let letters = ''
while (num >= 0) {
letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[num % 26] + letters
num = Math.floor(num / 26) - 1
}
return letters;
}
}
I could be wrong but I've checked the other functions in this answer and they seem to fail at 26 which should be Z. Remember there are 26 letters in the alphabet not 25.

Related

Replace / mask alternative N chars in a string

I want to replace some characters in a string with a "*", in the following way:
Given N, leave the first N characters as-is, but mask the next N characters with "*", then leave the next N characters unchanged, ...etc, alternating every N characters in the string.
I am able to mask every alternating character with "*" (the case where N is 1):
let str = "abcdefghijklmnopqrstuvwxyz"
for (let i =0; i<str.length; i +=2){
str = str.substring(0, i) + '*' + str.substring(i + 1);
}
console.log(str)
Output:
"*b*d*f*h*j*l*n*p*r*t*v*x*z"
But I don't know how to perform the mask with different values for N.
Example:
let string = "9876543210"
N = 1; Output: 9*7*5*3*1*
N = 2; Output: 98**54**10
N = 3; Output: 987***321*
What is the best way to achieve this without regular expressions?
You could use Array.from to map each character to either "*" or the unchanged character, depending on the index. If the integer division of the index by n is odd, it should be "*". Finally turn that array back to string with join:
function mask(s, n) {
return Array.from(s, (ch, i) => Math.floor(i / n) % 2 ? "*" : ch).join("");
}
let string = "9876543210";
console.log(mask(string, 1));
console.log(mask(string, 2));
console.log(mask(string, 3));
This code should work:
function stars(str, n = 1) {
const parts = str.split('')
let num = n
let printStars = false
return parts.map((letter) => {
if (num > 0 && !printStars) {
num -= 1
return letter
}
printStars = true
num += 1
if (num === n) {
printStars = false
}
return '*'
}).join('')
}
console.log(stars('14124123123'), 1)
console.log(stars('14124123123', 2), 2)
console.log(stars('14124123123', 3), 3)
console.log(stars('14124123123', 5), 5)
console.log(stars(''))
Cheers
This requires you to use the current mask as an argument and build your code upon it.
I also edited the function to allow other characters than the "*"
const number = 'abcdefghijklmnopqrstuvwxyzzz';
for(let mask = 1; mask <= 9; mask++){
console.log("Current mask:", mask, " Value: ",applyMask(number, mask));
}
function applyMask(data, mask, defaultMask = '*'){
// start i with the value of mask as we allow the first "n" characters to appear
let i;
let str = data;
for(i = mask; i < data.length ; i+=mask*2){
// I used the same substring method you used the diff is that i used the mask to get the next shown values
// HERE
str = str.substring(0, i) + defaultMask.repeat(mask) + str.substring(i + mask);
}
// this is to trim the string if any extra "defaultMask" were found
// this is to handle the not full size of the mask input at the end of the string
return str.slice(0, data.length)
}

Duplicating each character for all permutations of a string

This is a slightly odd/unique request. I am trying to achieve a result where e.g "yes" becomes, "yyes", "yees", "yess", "yyees", "yyess", "yyeess".
I have looked at this: Find all lowercase and uppercase combinations of a string in Javascript which completes it for capitalisation, however my understanding is prohibiting me from manipulating this into character duplication (if this method is even possible to use in this way).
export function letterDuplication(level: number, input: string){
const houseLength = input.length;
if (level == 1){
var resultsArray: string[] = [];
const letters = input.split("");
const permCount = 1 << input.length;
for (let perm = 0; perm < permCount; perm++) {
// Update the capitalization depending on the current permutation
letters.reduce((perm, letter, i) => {
if (perm & 1) {
letters[i] = (letter.slice(0, perm) + letter.slice(perm-1, perm) + letter.slice(perm));
} else {
letters[i] = (letter.slice(0, perm - 1) + letter.slice(perm, perm) + letter.slice(perm))
}
return perm >> 1;
}, perm);
var result = letters.join("");
console.log(result);
resultsArray[perm] = result;
}
If I haven't explained this particularly well please let me know and I'll clarify. I'm finding it quite the challenge!
General idea
To get the list of all words we can get from ordered array of letters, we need to get all combinations of letters, passed 1 or 2 times into word, like:
word = 'sample'
array = 's'{1,2} + 'a'{1,2} + 'm'{1,2} + 'p'{1,2} + 'l'{1,2} + 'e'{1,2}
Amount of all possible words equal to 2 ^ word.length (8 for "yes"), so we can build binary table with 8 rows that represent all posible combinations just via convering numbers from 0 to 7 (from decimal to binary):
0 -> 000
1 -> 001
2 -> 010
3 -> 011
4 -> 100
5 -> 101
6 -> 110
7 -> 111
Each decimal we can use as pattern for new word, where 0 means letter must be used once, and 1 - letter must be used twice:
0 -> 000 -> yes
1 -> 001 -> yess
2 -> 010 -> yees
3 -> 011 -> yeess
4 -> 100 -> yyes
5 -> 101 -> yyess
6 -> 110 -> yyees
7 -> 111 -> yyeess
Code
So, your code representation may looks like this:
// Initial word
const word = 'yes';
// List of all possible words
const result = [];
// Iterating (2 ^ word.length) times
for (let i = 0; i < Math.pow(2, word.length); i++) {
// Get binary pattern for each word
const bin = decToBin(i, word.length);
// Make a new word from pattern ...
let newWord = '';
for (let i = 0; i < word.length; i++) {
// ... by adding letter 1 or 2 times based on bin char value
newWord += word[i].repeat(+bin[i] ? 2 : 1);
}
result.push(newWord);
}
// Print result (all possible words)
console.log(result);
// Method for decimal to binary convertion with leading zeroes
// (always returns string with length = len)
function decToBin(x, len) {
let rem, bin = 0, i = 1;
while (x != 0) {
rem = x % 2;
x = parseInt(x / 2);
bin += rem * i;
i = i * 10;
}
bin = bin.toString();
return '0'.repeat(len - bin.length) + bin;
}
Maybe this example can help you. It's a bit not neat and not optimal, but it seems to work:
function getCombinations(word = '') {
const allCombination = [];
const generate = (n, arr, i = 0) => {
if (i === n) {
return allCombination.push([...arr]);
} else {
arr[i] = 0;
generate(n, arr, i + 1);
arr[i] = 1;
generate(n, arr, i + 1);
}
}
generate(word.length, Array(word.length).fill(0));
return allCombination.map((el) => {
return el.map((isCopy, i) => isCopy ? word[i].repeat(2) : word[i]).join('')
});
}
console.log(getCombinations('yes'));
console.log(getCombinations('cat'));

JavaScript script to detect Gibberish

I built an application to suggest email addresses fixes, and I need to detect email addresses that are basically not real existing email addresses, like the following:
14370afcdc17429f9e418d5ffbd0334a#magic.com
ce06e817-2149-6cfd-dd24-51b31e93ea1a#stackoverflow.org.il
87c0d782-e09f-056f-f544-c6ec9d17943c#microsoft.org.il
root#ns3160176.ip-151-106-35.eu
ds4-f1g-54-h5-dfg-yk-4gd-htr5-fdg5h#outlook.com
h-rt-dfg4-sv6-fg32-dsv5-vfd5-ds312#gmail.com
test#454-fs-ns-dff4-xhh-43d-frfs.com
I could do multi regex checks, but I don't think I will hit the good rate % of the suspected 'not-real' email addresses, as I go to a specific regex pattern each time.
I looked in:
Javascript script to find gibberish words in form inputs
Translate this JavaScript Gibberish please?
Detect keyboard mashed email addresses
Finally I looked over this:
Unable to detect gibberish names using Python
And It seems to fit my needs, I think. A script that will give me some score about the possibility of the each part of the email address to be a Gibberish (or not real) email address.
So what I want is the output to be:
const strings = ["14370afcdc17429f9e418d5ffbd0334a", "gmail", "ce06e817-2149-6cfd-dd24-51b31e93ea1a",
"87c0d782-e09f-056f-f544-c6ec9d17943c", "space-max", "ns3160176.ip-151-106-35",
"ds4-f1g-54-h5-dfg-yk-4gd-htr5-fdg5h", "outlook", "h-rt-dfg4-sv6-fg32-dsv5-vfd5-
ds312", "system-analytics", "454-fs-ns-dff4-xhh-43d-frfs"];
for (let i = 0; i < strings.length; i++) {
validateGibbrish(strings[i]);
}
And this validateGibberish function logic will be similar to this python code:
from nltk.corpus import brown
from collections import Counter
import numpy as np
text = '\n'.join([' '.join([w for w in s]) for s in brown.sents()])
unigrams = Counter(text)
bigrams = Counter(text[i:(i+2)] for i in range(len(text)-2))
trigrams = Counter(text[i:(i+3)] for i in range(len(text)-3))
weights = [0.001, 0.01, 0.989]
def strangeness(text):
r = 0
text = ' ' + text + '\n'
for i in range(2, len(text)):
char = text[i]
context1 = text[(i-1):i]
context2 = text[(i-2):i]
num = unigrams[char] * weights[0] + bigrams[context1+char] * weights[1] + trigrams[context2+char] * weights[2]
den = sum(unigrams.values()) * weights[0] + unigrams[char] + weights[1] + bigrams[context1] * weights[2]
r -= np.log(num / den)
return r / (len(text) - 2)
So in the end I will loop on all the strings and get something like this:
"14370afcdc17429f9e418d5ffbd0334a" -> 8.9073
"gmail" -> 1.0044
"ce06e817-2149-6cfd-dd24-51b31e93ea1a" -> 7.4261
"87c0d782-e09f-056f-f544-c6ec9d17943c" -> 8.3916
"space-max" -> 1.3553
"ns3160176.ip-151-106-35" -> 6.2584
"ds4-f1g-54-h5-dfg-yk-4gd-htr5-fdg5h" -> 7.1796
"outlook" -> 1.6694
"h-rt-dfg4-sv6-fg32-dsv5-vfd5-ds312" -> 8.5734
"system-analytics" -> 1.9489
"454-fs-ns-dff4-xhh-43d-frfs" -> 7.7058
Does anybody have a hint how to do it and can help?
Thanks a lot :)
UPDATE (12-22-2020)
I manage to write some code based on #Konstantin Pribluda answer, the Shannon entropy calculation:
const getFrequencies = str => {
let dict = new Set(str);
return [...dict].map(chr => {
return str.match(new RegExp(chr, 'g')).length;
});
};
// Measure the entropy of a string in bits per symbol.
const entropy = str => getFrequencies(str)
.reduce((sum, frequency) => {
let p = frequency / str.length;
return sum - (p * Math.log(p) / Math.log(2));
}, 0);
const strings = ['14370afcdc17429f9e418d5ffbd0334a', 'or', 'sdf', 'test', 'dave coperfield', 'gmail', 'ce06e817-2149-6cfd-dd24-51b31e93ea1a',
'87c0d782-e09f-056f-f544-c6ec9d17943c', 'space-max', 'ns3160176.ip-151-106-35',
'ds4-f1g-54-h5-dfg-yk-4gd-htr5-fdg5h', 'outlook', 'h-rt-dfg4-sv6-fg32-dsv5-vfd5-ds312', 'system-analytics', '454-fs-ns-dff4-xhh-43d-frfs'];
for (let i = 0; i < strings.length; i++) {
const str = strings[i];
let result = 0;
try {
result = entropy(str);
}
catch (error) { result = 0; }
console.log(`Entropy of '${str}' in bits per symbol:`, result);
}
The output is:
Entropy of '14370afcdc17429f9e418d5ffbd0334a' in bits per symbol: 3.7417292966721747
Entropy of 'or' in bits per symbol: 1
Entropy of 'sdf' in bits per symbol: 1.584962500721156
Entropy of 'test' in bits per symbol: 1.5
Entropy of 'dave coperfield' in bits per symbol: 3.4565647621309536
Entropy of 'gmail' in bits per symbol: 2.3219280948873626
Entropy of 'ce06e817-2149-6cfd-dd24-51b31e93ea1a' in bits per symbol: 3.882021446536749
Entropy of '87c0d782-e09f-056f-f544-c6ec9d17943c' in bits per symbol: 3.787301737252941
Entropy of 'space-max' in bits per symbol: 2.94770277922009
Entropy of 'ns3160176.ip-151-106-35' in bits per symbol: 3.1477803284561103
Entropy of 'ds4-f1g-54-h5-dfg-yk-4gd-htr5-fdg5h' in bits per symbol: 3.3502926596166693
Entropy of 'outlook' in bits per symbol: 2.1280852788913944
Entropy of 'h-rt-dfg4-sv6-fg32-dsv5-vfd5-ds312' in bits per symbol: 3.619340871812292
Entropy of 'system-analytics' in bits per symbol: 3.327819531114783
Entropy of '454-fs-ns-dff4-xhh-43d-frfs' in bits per symbol: 3.1299133176846836
It's still not working as expected, as 'dave coperfield' gets about the same points as other gibberish results.
Anyone else have better logic or ideas on how to do it?
This is what I come up with:
// gibberish detector js
(function (h) {
function e(c, b, a) { return c < b ? (a = b - c, Math.log(b) / Math.log(a) * 100) : c > a ? (b = c - a, Math.log(100 - a) / Math.log(b) * 100) : 0 } function k(c) { for (var b = {}, a = "", d = 0; d < c.length; ++d)c[d] in b || (b[c[d]] = 1, a += c[d]); return a } h.detect = function (c) {
if (0 === c.length || !c.trim()) return 0; for (var b = c, a = []; a.length < b.length / 35;)a.push(b.substring(0, 35)), b = b.substring(36); 1 <= a.length && 10 > a[a.length - 1].length && (a[a.length - 2] += a[a.length - 1], a.pop()); for (var b = [], d = 0; d < a.length; d++)b.push(k(a[d]).length); a = 100 * b; for (d = b =
0; d < a.length; d++)b += parseFloat(a[d], 10); a = b / a.length; for (var f = d = b = 0; f < c.length; f++) { var g = c.charAt(f); g.match(/^[a-zA-Z]+$/) && (g.match(/^(a|e|i|o|u)$/i) && b++, d++) } b = 0 !== d ? b / d * 100 : 0; c = c.split(/[\W_]/).length / c.length * 100; a = Math.max(1, e(a, 45, 50)); b = Math.max(1, e(b, 35, 45)); c = Math.max(1, e(c, 15, 20)); return Math.max(1, (Math.log10(a) + Math.log10(b) + Math.log10(c)) / 6 * 100)
}
})("undefined" === typeof exports ? this.gibberish = {} : exports)
// email syntax validator
function validateSyntax(email) {
return /^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(email.toLowerCase());
}
// shannon entropy
function entropy(str) {
return Object.values(Array.from(str).reduce((freq, c) => (freq[c] = (freq[c] || 0) + 1) && freq, {})).reduce((sum, f) => sum - f / str.length * Math.log2(f / str.length), 0)
}
// vowel counter
function countVowels(word) {
var m = word.match(/[aeiou]/gi);
return m === null ? 0 : m.length;
}
// dummy function
function isTrue(value){
return value
}
// validate string by multiple tests
function detectGibberish(str){
var strWithoutPunct = str.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g,"");
var entropyValue = entropy(str) < 3.5;
var gibberishValue = gibberish.detect(str) < 50;
var vovelValue = 30 < 100 / strWithoutPunct.length * countVowels(strWithoutPunct) && 100 / strWithoutPunct.length * countVowels(str) < 35;
return [entropyValue, gibberishValue, vovelValue].filter(isTrue).length > 1
}
// main function
function validateEmail(email) {
return validateSyntax(email) ? detectGibberish(email.split("#")[0]) : false
}
// tests
document.write(validateEmail("dsfghjdhjs#gmail.com") + "<br/>")
document.write(validateEmail("jhon.smith#gmail.com"))
I have combined multiple tests: gibberish-detector.js, Shannon entropy and counting vowels (between 30% and 35%). You can adjust some values for more accurate result.
A thing you may consider doing is checking each time how random each string is, then sort the results according to their score and given a threshold exclude the ones with high randomness. It is inevitable that you will miss some.
There are some implementations for checking the randomness of strings, for example:
https://en.wikipedia.org/wiki/Diehard_tests
http://www.cacert.at/random/
You may have to create a hash (to map chars and symbols to sequences of integers) before you apply some of these because some work only with integers, since they test properties of random numbers generators.
Also a stack exchange link that can be of help is this:
https://stats.stackexchange.com/questions/371150/check-if-a-character-string-is-not-random
PS. I am having a similar problem in a service since robots create accounts with these type of fake emails. After years of dealing with this issue (basically deleting manually from the DB the fake emails) I am now considering introducing a visual check (captcha) in the signup page to avoid the frustration.

Searching through a repeated string

Lilah has a string, s, of lowercase English letters that she repeated infinitely many times.
Given an integer, n, find and print the number of letter 'a' in the first n letters of Lilah's infinite string.
This is my solution, but it is not correct, and I'm struggling to figure out why:
function repeatedString(s, n) {
let counter = 0;
const remainder = n % s.length;
const substring = s.substring(0, remainder);
const concatString = s + substring;
for (let letter of concatString) {
if (letter === 'a') {
counter++;
}
}
return (counter * n);
}
const str = "dhfgjhdfoiahwiuerhiguhzlkjvxzlkjghatriaeriaauih";
console.log(
repeatedString(str, 20)
);
I think it may be the
const concatString = s + substring;
would you please just reference the substring instead...
for (let letter of substring) {
if (letter === 'a') {
counter++;
}
}
return counter
You just need to loop through the s.substring(0, n) and return the counter value, (counter * n) doesn't make any sense.
function repeatedString(s, n) {
let counter = 0;
//const remainder = n % s.length;
const substring = s.substring(0, n);
console.log(substring);
//const concatString = s + substring;
for (let letter of substring) {
if (letter === 'a') {
counter++;
}
}
return counter;
}
const str = "dhfgjhdfoiahwiuerhiguhzlkjvxzlkjghatriaeriaauih";
console.log(
repeatedString(str, 20)
);
You can do that in following steps:
Get the count of the letter in given string. i.e c1.
Then get the part of the substring using slice(). The part will start of 0 up the the remainder of n and length of string.(c2)
Then multiply c1 with the number of times the given string will be in string of length n. i.e c1 * Math.floor(n/str.length)
Add the other count of remaining part c2 to the result and return
You can do that using filter() and check the count of given letter in given string. And then multiply it with no of times the string will repeat in for length n.
function func(str,l,n){
let c1 = [...str].filter(x => x === l).length;
let c2 = [...str.slice(0,n%str.length)].filter(x => x === l).length;
return (c1 * Math.floor(n/str.length)) + c2;
}
console.log(func('abcac','a',10))
This should give you the number of times a appears in the numbers of n length
const input = s;
var subStr = input.substr(0,n).split('');
console.log(subStr);
var output = 0;
subStr.forEach((e) => {
if (e === 'a') {
console.log(e);
output++;
}
})
console.log(output);
Using the next link as the source of the question:
https://medium.com/#yashka.troy/want-to-know-the-major-player-in-programming-18f2d35d91f7
Where it is explained better:
Lilah has a string, s, of lowercase English letters that she repeated infinitely many times.
Given an integer, n, find and print the number of letter a's in the first letters of Lilah's infinite string.
For example, if the string s=’abcac’ and n=10, the sub string we consider is ‘abcacabcac’, the first 10 characters of her infinite string. There are 4 occurrences of a in the substring.
A solution could be:
function repeatedString(s, n)
{
let res = 0;
const remainder = s.slice(0, n % s.length);
// Get number of times 'a' is present on "n / s.length" repetitions.
for (let letter of s)
{
if (letter === 'a') res++;
}
res *= Math.floor(n / s.length);
// Add the number of times 'a' is present on the remainder.
for (let letter of remainder)
{
if (letter === 'a') res++;
}
return res;
}
const str = "abca";
console.log(`${repeatedString(str, 10)} a's on first 10 letters:`);
console.log(`${repeatedString(str, 4)} a's on first 4 letters:`);
console.log(`${repeatedString(str, 0)} a's on first 0 letters:`);
console.log(`${repeatedString(str, 22)} a's on first 22 letters:`);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

How can we get the number of all possible non-negative integers similar to a given non-negative integer by re-arranging it using javascript

Given a non-negative number say 1213, it should return 12 because there are 12 possible integers similar to 1213 i.e., 1123,1132,1213,1231,1312,1321,2113,2131,2311,312,3121 and 3211. Same with 10, it should return 1 and 12 should return 2 and if the number is 120 it should return 4 as combinations are 120,102,210,201.
You can use this formula to get the total number of unique permutations excluding permutations with leading zero.
Lets define some symbols:
n = Total Number of digits
z = Number of zeros
r1, r2, ..., rn = repetition count of digits with count > 1
fact(p) = factorial of number of p
Total permutations = (n - z) * fact(n - 1) / fact(r1) * fact(r2) * .... * fact(rn)
For example, for 1213,
n = 4, z = 0, r1 (digit 1) = 2
permutations = (4 - 0) * fact(4 - 1) / fact(2) = 4 * 6 / 2 = 12
You can easily convert this to program.
function factorial(n) {
if (n <=1)
return 1;
return n * factorial(n-1);
}
function getPermutations(number) {
var n = number.toString().split('').length;
var r = {};
number.toString().split('').forEach(function(digit){
r[digit] = r[digit] || 0;
r[digit] += 1;
});
var z = number.toString().split('').reduce(function(count, digit) {
return (digit === '0') ? count + 1 : count;
}, 0);
var denominator = Object.keys(r).map(function (key) { return r[key]; }).reduce(function(result, curr) {
return result * factorial(curr);
}, 1);
//console.log(n, r, z);
return (n - z) * factorial(n - 1) / denominator;
}
var result = getPermutations(1216);
console.log(result);
Note : This is basic implementation and would not be the most optimum. Also, factorial calculation involves large numbers and would probably fail for large inputs.
You are looking for an anagram algorithm :
This script find every anagram of a string then delete every number starting with zero :
var allAnagrams = function(arr) {
var anagrams = {};
arr.forEach(function(str) {
var recurse = function(ana, str) {
if (str === '')
anagrams[ana] = 1;
for (var i = 0; i < str.length; i++)
recurse(ana + str[i], str.slice(0, i) + str.slice(i + 1));
};
recurse('', str);
});
return Object.keys(anagrams);
}
var arr = ['120']; //declare your number
var anag = allAnagrams(arr); //send it to the function
for (var i in anag) { //delete leading 0
if((anag[i].charAt(0)) === '0' ) {
anag.splice(i);
}
}
console.log(anag); //print array
console.log(anag.length); // print length
Here the output will be :
["102", "120", "201", "210"]
4

Categories