Is there a way to pass a range of numbers to startsWith? - javascript

I have an assignment to write a program that detects credit card networks, given a string of numbers. The way these are detected is by the prefix and the length. One credit card company uses 800 different sequential prefixes, and I'm wondering if there's a way to do this without writing 800 if statements. Seems like not something they'd assign.
edit: Regex is not allowed

Slice off the first 3 characters of the string, convert it to a number, and check that the number is within the range. Something like:
const verify = (str) => {
const first3 = str.slice(0, 3);
if (first3.length < 3) return false; // string is too short
const num = Number(first3);
if (Number.isNaN(num)) return false; // doesn't start with numbers
const result = num < 900 && num > 100; // check that it's between 100 and 900
console.log(result);
};
verify('92546');
verify('22546');

You could use something like that:
const startsWithSomeOf = (str, prefixes) => prefixes.some(prefix => str.startsWith(prefix));
console.log(startsWithSomeOf('123456', ['123', '111']));
console.log(startsWithSomeOf('abcdef', ['123', '111']));

Related

Slice method indexing issue in Javascript

I have a string as an input in the form; lets say "1,5;6,10". Now, I want to compare the number at position 1 and 3 .i.e.(1 & 6). Whichever one is largest the number right to it would be printed. In this case the number 10 would be printed as 1 < 6.
Let the input is,
const customer_demand ="1,5;6,10";
I want to procced with slice() method and separate 1 and 6 with:
const number1 = customer_demand.slice(0, 1); // 1
const number2 = customer_demand.slice(4, 5); // 6
and compare the resultants with if & else. But there may be a case when the third number is two digit like:
const customer_demand ="1,5;16,10";
my slice() method index would go offset. What can I do in this regard? I hope I have made myself clear, if not please leave a comment. Thanks
In your case it's better to use split:
const customer_demand ="1,5;16,10";
const number1 = customer_demand.split(";")[0].split(",")[0]; // 1
const number2 = customer_demand.split(";")[1].split(",")[0]; // 16
Also if you want them to be Numbers don't forget to cast it using parseInt.
The solution, use split. Here's an example
const customer_demand ="1,5;16,10";
function parseNumbers(string){
return string.split(";") //returns stuff like ["1,5", "16,10"]
.map(axis=>
axis.split(",") //["1", "5"]
.map(n=>parseInt(n)) //[1,5]
)
}
//example usage
const parsedDemand=parseNumbers(customer_demand)
const [number1,number2,number3,number4]=parsedDemand
console.log(parsedDemand)
Make your life easier and break up your strings into managable arrays. Here is an example of when you don't know how many sets of numbers to compare ahead of time.
const customer_demand ="1,5;16,10";
// the following should also work for data like: "1,3,4,7;1,44;100"
let answers = [];
customer_demand.split(";").forEach( set => {
let setitems = set.split(",");
let biggest = setitems.reduce(function(a, b) {
return Math.max(Number(a), Number(b));
});
answers.push(biggest)
});
// answers is now an array - each item is the biggest number of that set. In your example it would be [5,16]

How to generate a fixed-length code from a set of integers of a specific bit count in JavaScript

Generate string from integer with arbitrary base in JavaScript received the following answer:
function parseInt(value, code) {
return [...value].reduce((r, a) => r * code.length + code.indexOf(a), 0);
}
function toString(value, code) {
var digit,
radix= code.length,
result = '';
do {
digit = value % radix;
result = code[digit] + result;
value = Math.floor(value / radix);
} while (value)
return result;
}
console.log(parseInt('dj', 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
console.log(toString(123, 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
console.log(parseInt('a', 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
console.log(toString(0, 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
I am interested something slightly different. Whereas this will generate the shortest code for the number, I would like to now generate a constant-length code based on the number of bits. I am not sure if this is also a complex radix solution as well.
Say I want to generate 8-bit codes using a 16-character alphabet. That means I should be able to take the first 4 bits to select 1 character, and the next 4 bits to select the second character. So I might end up with MV if my 16 character set was ABDHNMOPQRSTUVYZ. Likewise if I had a 16-bit range, I would have 4 character code, and 32-bit range would be an 8-character code. So calling code32(1, 'ABDHNMOPQRSTUVYZ') would give an 8 letter code, while code8(1, 'ABDHNMOPQRSTUVYZ') would give a 2 digit code.
How could that be implemented in JavaScript? Something along these lines?
code8(i, alpha) // 0 to 255 it accepts
code16(i, alpha) // 0 to 65535 it accepts
code32(i, alpha) // 0 to 2^32-1 it accepts
Likewise, how would you get the string code back into the original number (or bit sequence)?
This really comes down to changing toString so that:
It only accepts a code that has a length of a power of 2
It pads the result to a given number of "digits" (characters)
The actual number of digits you would use for a 16 bit number depends on the size of the code. If the code has 16 characters, then it can cover for 4 bits, and so an output of 4 characters would be needed. If however the code has 4 characters, then the output would need 8 characters. You can have cases where the match is not exact, like when you would have a code with 8 characters. Then the output would need 6 characters.
Here I have highlighted the changes to the toString method. My personal preference is to also put the value as last parameter to toString.
function toString(digitCount, code, value) { // <-- add argument digitCount
// Perform a sanity check: code must have a length that is power of 2
if (Math.log2(code.length) % 1) throw "code size not power of 2: " + code.length;
var digit,
radix = code.length,
result = '';
do {
digit = value % radix;
result = code[digit] + result;
value = Math.floor(value / radix);
} while (value)
return result.padStart(digitCount, code[0]); // Pad to the desired output size
}
console.log(toString(4, 'abcdefghijklmnop', 123));
console.log(toString(4, 'abcdefghijklmnop', 0));
console.log(toString(4, 'abcdefghijklmnop', 0xFFFF));
// You could define some more specific functions
const code8 = (code, value) => toString(Math.ceil(8 / Math.log2(code.length)), code, value);
const code16 = (code, value) => toString(Math.ceil(16 / Math.log2(code.length)), code, value);
console.log(code16('abcdefghijklmnop', 123));
console.log(code16('abcdefghijklmnop', 0));
console.log(code16('abcdefghijklmnop', 0xFFFF));
console.log(code8('abcdefghijklmnop', 123));
console.log(code8('abcdefghijklmnop', 0));
console.log(code8('abcdefghijklmnop', 0xFF));
EDIT: I just noticed that you required a decoder as well. It is easy to implement a non-optimal version too, while an optimal one can be implemented via go through each letter and accumulate their value times their weighs.
Is this what you want? I tested this code for bit=16 and bit=8, but when bit=32 the count of codewords becomes too large and hangs the devtools of the browser. It's only a demonstrative code and may need optimization if need to be applied in practical use...
function genCode(len, alpha){
let tmp = [...alpha];
for(let i = 1; i != len; ++i){
const ttmp = [];
tmp.forEach(te => {
[...alpha].forEach(e => {
ttmp.push(te + e);
});
});
tmp = ttmp;
}
return tmp;
}
function code(bits, i, alpha){
const len = Math.ceil(bits / Math.floor(Math.log2(alpha.length)));
return genCode(len, alpha)[i];
}
function decode(bits, c, alpha){
const len = Math.ceil(bits / Math.floor(Math.log2(alpha.length)));
const codes = genCode(len, alpha);
return codes.indexOf(c);
}
console.log(code(16, 2, "ABDHNMOPQRSTUVYZ"));
console.log(decode(16, "AAAD", "ABDHNMOPQRSTUVYZ"));
console.log(code(8, 255, "ABDHNMOPQRSTUVYZ"));
console.log(decode(8, "ZZ", "ABDHNMOPQRSTUVYZ"));

Extracting certain substring from a string

I want to extract the last part of this string : "https://steamcommunity.com/profiles/76561198364464404".Just the numbers after '/profiles'.But the problem is the URL can change sometimes.
There are two types of url
1.First one is "https://steamcommunity.com/profiles/76561198364464404" with "/profiles" and then the "id"(id is the numbers after '/profiles').
2."https://steamcommunity.com/id/purotexnuk".Second is this type.Where "/profiles" doesn't exist.
I have come up this code :
let inc;
const index = 27;
const string = 'https://steamcommunity.com/id/purotexnuk';
if (string.includes('profiles')) {
inc = 9;
} else {
inc = 3;
}
console.log(string.slice(index + inc, -1));
The above code checks wheather the string "/profiles" is present.If the string contains "/profiles".inc will be 9.So that slice starts from the right side of the string(url) and ends at the first '/' from the right.inc is 9 becuase "profiles/" length is 9.Similar way if the string(url) contains "id".The slice will start from the end and stop at the first '/' from the right.inc will be 3 becuase "id/" length is 3.
The index is always constant because ,"/profiles" or "/id" only occurs after "https://steamcommunity.com" whose length is 27.Is there any better way i can extract only the profile id or profile name?
(profile id - 76561198364464404)
(profile name - purotexnuk )
You can use regex for this, it will also take care if your url ends with / or has query parameters example https://steamcommunity.com/id/purotexnuk?ref=abc
/.*(?:profiles|id)\/([a-z0-9]+)[\/?]?/i
example:
const regex = /.*(?:profiles|id)\/([a-z0-9]+)[\/?]?/i;
const matches = regex.exec('https://steamcommunity.com/id/purotexnuk');
console.log(matches[1]);
You can split the string with delimiter / and return the last value value from the array;
function getNum(str) {
const arr = str.split('/');
if (!isNaN(arr[arr.length - 1])) {
return arr[arr.length - 1];
}
return ' no number ';
}
const st1 = "https://steamcommunity.com/profiles/76561198364464404";
const st2 = "https://steamcommunity.com/profiles/76561198364464404";
const st3 = "https://steamcommunity.com/id/purotexnuk";
console.log(getNum(st1));
console.log(getNum(st2));
console.log(getNum(st3));
Or do it in one line:
const string = 'https://steamcommunity.com/id/purotexnuk';
console.log(string.slice(string.lastIndexOf("/") + 1, string.length));

Do a simple seach regardless of upper an lower case

Could someone explain to me, how I can do in javascript this simple code, without taking care of upper and lower case?
if(res.search('em')!=-1){ unit='em'; res.replace(unit,'');}
if(res.search('vh')!=-1){ unit='vh'; res.replace(unit,'');}
if(res.search('px')!=-1){ unit='px'; res.replace(unit,'');}
Without any idea, that is what I have coded. It's a lot of code
if(res.search('Em')!=-1){ unit='Em'; res.replace(unit,'');}
if(res.search('eM')!=-1){ unit='eM'; res.replace(unit,'');}
if(res.search('EM')!=-1){ unit='EM'; res.replace(unit,'');}
...
I'm sure there is a better way to do that!?
Thanks a lot.
You could use a regular expression with replace and save the found unit as a side effect of the replacer function. This would allow you to replace the unit without searching the string twice:
let res = "Value of 200Em etc."
let unit
let newRes = res.replace(/(em|vh|px)/i, (found) => {unit = found.toLowerCase(); return ''})
console.log("replaced:", newRes, "Found Unit:", unit)
For the first part you can use toLowerCase()
if(res.toLowerCase().search('em') != -1)
You can use alternation in regex alongside case insensitive flag.
/(em|vh|px)/i Mathces em or vh or px.
function replaceUnit(input){
return input.replace(/(em|px|vh)/i ,'replaced')
}
console.log(replaceUnit('height: 20em'))
console.log(replaceUnit('width:=20Em'))
console.log(replaceUnit('border-radius: 2Px'))
console.log(replaceUnit('unit=pX'))
console.log(replaceUnit('max-height=20Vh'))
you can use toLowerCase(), transform all the string to lower case and compare,
var tobereplaced = 'em';
if(res.search.toLowerCase(tobereplaced)> -1){ res.replace(tobereplaced,'');}
If you can make these three assumptions:
The string always starts with a number
The string always ends with a unit
The unit is always two characters
Then it could be as simple as:
const str = '11.5px';
const unit = str.substr(-2); //=> 'px'
const value = parseFloat(str, 10); //=> 11.5
Or with a function:
const parse = str => ({unit: str.substr(-2), value: parseFloat(str, 10)});
const {unit, value} = parse('11.5px');
// unit='px', value=11.5
All you need to to is force your string to lowercase (or uppercase) before testing its contents:
if( res.toLowerCase().search('em') !== -1){ do(stuff); }
To handle replacing the actual substring value in res, something like this should work:
let caseInsensitiveUnit = "em";
let unitLength;
let actualUnit;
let position = res.toLowerCase().search(caseInsensitiveUnit);
if(position > -1){
unitLength = caseInsensitiveUnit.length;
actualUnit = res.substring(postion, position + unitLength);
res.replace(actualUnit, "");
}

How do I shorten and expand a uuid to a 15 or less characters

Given a uuid(v4) without dashes, how can I shorten it to a 15 or less than 15 characters string? I should also be able to go back to the original uuid from the 15 characters string.
I am trying to shorten it to send it in a flat file and the file format specifies this field to be a 15 characters alphanumeric field. Given that shortened uuid, I should be able to map it back to the original uuid.
Here is what I tried, but definitely not what I wanted.
export function shortenUUID(uuidToShorten: string, length: number) {
const uuidWithoutDashes = uuidToShorten.replace(/-/g , '');
const radix = uuidWithoutDashes.length;
const randomId = [];
for (let i = 0; i < length; i++) {
randomId[i] = uuidWithoutDashes[ 0 | Math.random() * radix];
}
return randomId.join('');
}
As AuxTaco pointed out, if you actually mean "alphanumeric" as in it matches "/^[A-Za-z0-9]{0,15}/" (giving the number of bits of 26 + 26 + 10 = 62), then it is really impossible. You can't fit 3 gallons of water in a gallon bucket without losing something. A UUID is 128-bits, so to convert that to a character space of 62, you'd need at least 22 characters (log[base 62](2^128) == ~22).
If you are more flexible on your charset and just need it 15 unicode characters you can put in a text document, then my answer will help.
Note: First part of this answer, I thought it said length of 16, not 15. The simpler answer won't work. The more complex version below still will.
In order to do so, you'd to use some kind of two-way compression algorithm (similar to an algorithm that is used for zipping files).
However, the problem with trying to compress something like a UUID is you'd probably have lots of collisions.
A UUID v4 is 32 characters long (without dashes). It's hexadecimal, so it's character space is 16 characters (0123456789ABCDEF)
That gives you a number of possible combinations of 16^32, approximately 3.4028237e+38 or 340,282,370,000,000,000,000,000,000,000,000,000,000. To make it recoverable after compression, you'd have to make sure you don't have any collisions (i.e., no 2 UUIDs turn into the same value). That's a lot of possible values (which is exactly why we use that many for UUID, the chance of 2 random UUIDs is only 1 out of that number big number).
To crunch that many possibilities to 16 characters, you'd have to have at least as many possible values. With 16 characters, you'd have to have 256 characters (root 16 of that big number, 256^16 == 16^32`). That's assuming you have an algorithm that'd never create a collision.
One way to ensure you never have collisions would be to convert it from a base-16 number to a base-256 number. That would give you a 1-to-1 relation, ensuring no collisions and making it perfectly reversible. Normally, switching bases is easy in JavaScript: parseInt(someStr, radix).toString(otherRadix) (e.g., parseInt('00FF', 16).toString(20). Unfortunately, JavaScript only does up to a radix of 36, so we'll have to do the conversion ourselves.
The catch with such a large base is representing it. You could arbitrarily pick 256 different characters, throw them in a string, and use that for a manual conversion. However, I don't think there are 256 different symbols on a standard US keyboard, even if you treat upper and lowercase as different glyphs.
A simpler solution would be to just use arbitrary character codes from 0 to 255 with String.fromCharCode().
Another small catch is if we tried to treat that all as one big number, we'd have issues because it's a really big number and JavaScript can't properly represent it exactly.
Instead of that, since we already have hexadecimal, we can just split it into pairs of decimals, convert those, then spit them out. 32 hexadecimal digits = 16 pairs, so that'll (coincidentally) be perfect. (If you had to solve this for an arbitrary size, you'd have to do some extra math and converting to split the number into pieces, convert, then reassemble.)
const uuid = '1234567890ABCDEF1234567890ABCDEF';
const letters = uuid.match(/.{2}/g).map(pair => String.fromCharCode(parseInt(pair, 16)));
const str = letters.join('');
console.log(str);
Note that there are some random characters in there, because not every char code maps to a "normal" symbol. If what you are sending to can't handle them, you'll instead need to go with the array approach: find 256 characters it can handle, make an array of them, and instead of String.fromCharCode(num), use charset[num].
To convert it back, you would just do the reverse: get the char code, convert to hex, add them together:
const uuid = '1234567890ABCDEF1234567890ABCDEF';
const compress = uuid =>
uuid.match(/.{2}/g).map(pair => String.fromCharCode(parseInt(pair, 16))).join('');
const expand = str =>
str.split('').map(letter => ('0' + letter.charCodeAt(0).toString(16)).substr(-2)).join('');
const str = compress(uuid);
const original = expand(str);
console.log(str, original, original.toUpperCase() === uuid.toUpperCase());
For fun, here is how you could do it for any arbitrary input base and output base.
This code is a bit messy because it is really expanded to make it more self-explanatory, but it basically does what I described above.
Since JavaScript doesn't have an infinite level of precision, if you end up converting a really big number, (one that looks like 2.00000000e+10), every number not shown after that e was essentially chopped off and replaced with a zero. To account for that, you'll have to break it up in some way.
In the code below, there is a "simple" way which doesn't account for this, so only works for smaller strings, and then a proper way which breaks it up. I chose a simple, yet somewhat inefficient, approach of just breaking up the string based on how many digits it gets turned into. This isn't the best way (since math doesn't really work like that), but it does the trick (at the cost of needed a smaller charset).
You could imploy a smarter splitting mechanism if you really needed to keep your charset size to a minimum.
const smallStr = '1234';
const str = '1234567890ABCDEF1234567890ABCDEF';
const hexCharset = '0123456789ABCDEF'; // could also be an array
const compressedLength = 16;
const maxDigits = 16; // this may be a bit browser specific. You can make it smaller to be safer.
const logBaseN = (num, n) => Math.log(num) / Math.log(n);
const nthRoot = (num, n) => Math.pow(num, 1/n);
const digitsInNumber = num => Math.log(num) * Math.LOG10E + 1 | 0;
const partitionString = (str, numPartitions) => {
const partsSize = Math.ceil(str.length / numPartitions);
let partitions = [];
for (let i = 0; i < numPartitions; i++) {
partitions.push(str.substr(i * partsSize, partsSize));
}
return partitions;
}
console.log('logBaseN test:', logBaseN(256, 16) === 2);
console.log('nthRoot test:', nthRoot(256, 2) === 16);
console.log('partitionString test:', partitionString('ABCDEFG', 3));
// charset.length should equal radix
const toDecimalFromCharset = (str, charset) =>
str.split('')
.reverse()
.map((char, index) => charset.indexOf(char) * Math.pow(charset.length, index))
.reduce((sum, num) => (sum + num), 0);
const fromDecimalToCharset = (dec, charset) => {
const radix = charset.length;
let str = '';
for (let i = Math.ceil(logBaseN(dec + 1, radix)) - 1; i >= 0; i--) {
const part = Math.floor(dec / Math.pow(radix, i));
dec -= part * Math.pow(radix, i);
str += charset[part];
}
return str;
};
console.log('toDecimalFromCharset test 1:', toDecimalFromCharset('01000101', '01') === 69);
console.log('toDecimalFromCharset test 2:', toDecimalFromCharset('FF', hexCharset) === 255);
console.log('fromDecimalToCharset test:', fromDecimalToCharset(255, hexCharset) === 'FF');
const arbitraryCharset = length => new Array(length).fill(1).map((a, i) => String.fromCharCode(i));
// the Math.pow() bit is the possible number of values in the original
const simpleDetermineRadix = (strLength, originalCharsetSize, compressedLength) => nthRoot(Math.pow(originalCharsetSize, strLength), compressedLength);
// the simple ones only work for values that in decimal are so big before lack of precision messes things up
// compressedCharset.length must be >= compressedLength
const simpleCompress = (str, originalCharset, compressedCharset, compressedLength) =>
fromDecimalToCharset(toDecimalFromCharset(str, originalCharset), compressedCharset);
const simpleExpand = (compressedStr, originalCharset, compressedCharset) =>
fromDecimalToCharset(toDecimalFromCharset(compressedStr, compressedCharset), originalCharset);
const simpleNeededRadix = simpleDetermineRadix(str.length, hexCharset.length, compressedLength);
const simpleCompressedCharset = arbitraryCharset(simpleNeededRadix);
const simpleCompressed = simpleCompress(str, hexCharset, simpleCompressedCharset, compressedLength);
const simpleExpanded = simpleExpand(simpleCompressed, hexCharset, simpleCompressedCharset);
// Notice, it gets a little confused because of a lack of precision in the really big number.
console.log('Original string:', str, toDecimalFromCharset(str, hexCharset));
console.log('Simple Compressed:', simpleCompressed, toDecimalFromCharset(simpleCompressed, simpleCompressedCharset));
console.log('Simple Expanded:', simpleExpanded, toDecimalFromCharset(simpleExpanded, hexCharset));
console.log('Simple test:', simpleExpanded === str);
// Notice it works fine for smaller strings and/or charsets
const smallCompressed = simpleCompress(smallStr, hexCharset, simpleCompressedCharset, compressedLength);
const smallExpanded = simpleExpand(smallCompressed, hexCharset, simpleCompressedCharset);
console.log('Small string:', smallStr, toDecimalFromCharset(smallStr, hexCharset));
console.log('Small simple compressed:', smallCompressed, toDecimalFromCharset(smallCompressed, simpleCompressedCharset));
console.log('Small expaned:', smallExpanded, toDecimalFromCharset(smallExpanded, hexCharset));
console.log('Small test:', smallExpanded === smallStr);
// these will break the decimal up into smaller numbers with a max length of maxDigits
// it's a bit browser specific where the lack of precision is, so a smaller maxDigits
// may make it safer
//
// note: charset may need to be a little bit bigger than what determineRadix decides, since we're
// breaking the string up
// also note: we're breaking the string into parts based on the number of digits in it as a decimal
// this will actually make each individual parts decimal length smaller, because of how numbers work,
// but that's okay. If you have a charset just barely big enough because of other constraints, you'll
// need to make this even more complicated to make sure it's perfect.
const partitionStringForCompress = (str, originalCharset) => {
const numDigits = digitsInNumber(toDecimalFromCharset(str, originalCharset));
const numParts = Math.ceil(numDigits / maxDigits);
return partitionString(str, numParts);
}
const partitionedPartSize = (str, originalCharset) => {
const parts = partitionStringForCompress(str, originalCharset);
return Math.floor((compressedLength - parts.length - 1) / parts.length) + 1;
}
const determineRadix = (str, originalCharset, compressedLength) => {
const parts = partitionStringForCompress(str, originalCharset);
return Math.ceil(nthRoot(Math.pow(originalCharset.length, parts[0].length), partitionedPartSize(str, originalCharset)));
}
const compress = (str, originalCharset, compressedCharset, compressedLength) => {
const parts = partitionStringForCompress(str, originalCharset);
const partSize = partitionedPartSize(str, originalCharset);
return parts.map(part => simpleCompress(part, originalCharset, compressedCharset, partSize)).join(compressedCharset[compressedCharset.length-1]);
}
const expand = (compressedStr, originalCharset, compressedCharset) =>
compressedStr.split(compressedCharset[compressedCharset.length-1])
.map(part => simpleExpand(part, originalCharset, compressedCharset))
.join('');
const neededRadix = determineRadix(str, hexCharset, compressedLength);
const compressedCharset = arbitraryCharset(neededRadix);
const compressed = compress(str, hexCharset, compressedCharset, compressedLength);
const expanded = expand(compressed, hexCharset, compressedCharset);
console.log('String:', str, toDecimalFromCharset(str, hexCharset));
console.log('Neded radix size:', neededRadix); // bigger than normal because of how we're breaking it up... this could be improved if needed
console.log('Compressed:', compressed);
console.log('Expanded:', expanded);
console.log('Final test:', expanded === str);
To use the above specifically to answer the question, you would use:
const hexCharset = '0123456789ABCDEF';
const compressedCharset = arbitraryCharset(determineRadix(uuid, hexCharset));
// UUID to 15 characters
const compressed = compress(uuid, hexCharset, compressedCharset, 15);
// 15 characters to UUID
const expanded = expanded(compressed, hexCharset, compressedCharset);
If there are problematic characters in the arbitrary, you'll have to do something to either filter those out, or hard-code a specific one. Just make sure all of the functions are deterministic (i.e., same result every time).

Categories

Resources