I'm trying to convert strings that match /(\d)+(\.\d+)?(m|g|t)?b?/i into bytes.
For example, 1KB would return 1024. 1.2mb would return 1258291.
If you reorganize the capturing group in your regex like so: /(\d+(?:\.\d+)?)\s?(k|m|g|t)?b?/i
you can do something like:
function unhumanize(text) {
var powers = {'k': 1, 'm': 2, 'g': 3, 't': 4};
var regex = /(\d+(?:\.\d+)?)\s?(k|m|g|t)?b?/i;
var res = regex.exec(text);
return res[1] * Math.pow(1024, powers[res[2].toLowerCase()]);
}
unhumanize('1 Kb')
# 1024
unhumanize('1 Mb')
# 1048576
unhumanize('1 Gb')
# 1073741824
unhumanize('1 Tb')
# 1099511627776
You've already got a capturing group for the unit prefix, now all you need is a lookup table:
{ 'k', 1L<<10 },
{ 'M', 1L<<20 },
{ 'G', 1L<<30 },
{ 'T', 1L<<40 },
{ 'P', 1L<<50 },
{ 'E', 1L<<60 }
Demo: http://ideone.com/5O7Vp
Although 1258291 is clearly far too many significant digits to get from 1.2MB.
oops, I gave a C# example. The method is still good though.
One liner solution:
"1.5 MB".replace(/(\d+)+(\.(\d+))?\s?(k|m|g|t)?b?/i, function(value, p1, p2, p3, p4) { return parseFloat(p1 + (p2 || ""))*({ 'K' : 1<<10, 'M' : 1<<20, 'G' : 1<<30, 'T' : 1<<40 }[p4] || 1); })
# 1572864
Related
I'm a super noob, and I'm trying to make a Caesar cipher in p5js, so far I manage to code the UI, but now I'm stuck and don't really know how to move forward can someone please help?
I know I need to use for loops, but I can't figure out how?
I really appreciate all the help
Thanks
let inp;
let button;
let alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
function setup() {
createCanvas(600, 600);
// Type here your plain or encryted message
inp = createInput();
inp.position(20, 30);
inp.size(550, 200);
// Encrypted / Decrypted message
inp = createInput();
inp.position(20, 340);
inp.size(550, 200);
// Key
inp = createInput();
inp.position(20, 280);
inp.size(200, 20);
button = createButton("Encrypt");
button.position(370, 260);
button.size(100, 50);
// button.mousePressed(encrypt);
button = createButton("Decrypt");
button.position(475, 260);
button.size(100, 50);
// button.mousePressed(decrypt);
noStroke();
// button.mousePressed(drawName);
}
function draw() {
background(0)
text("Type here your plain or encryted message", 20, 20);
text("Encrypted / Decrypted message", 20, 330);
text("Key", 20, 270);
fill(255)
}
Here's a link to the completed version: https://editor.p5js.org/Samathingamajig/sketches/7P5e__R8M
But I'll actually explain what I did so that you gain something from this.
How a Caesar Cipher works:
Start with a seed value, and integer of how much to rotate the text
To encrypt, for each letter add the seed to the letter. i.e. A + 2 = C, B + 5 = G, etc.
To decrypt, for each letter subtract the seed from the letter. i.e. C - 2 = A, G - 5 = B, etc.
Pseudo code:
function encrypt():
initial text = (get initial text)
output = "" // empty string
offset (aka seed) = (get the seed, make sure its an integer) mod 26
for each character in initial:
if character is in the alphabet:
index = (find the index of the character in the alphabet)
outputIndex = (index + offset + 26 /* this 26 isn't needed, but it's there to make the decrypt be a simple copy + paste, you'll see */ ) mod 26 /* force between 0 and 25, inclusive */
output += outputIndex to character
else:
output += initial character
(set the output text field to the output)
function decrypt():
initial text = (get initial text)
output = "" // empty string
offset (aka seed) = (get the seed, make sure its an integer)
for each character in initial:
if character is in the alphabet:
index = (find the index of the character in the alphabet)
outputIndex = (index - offset + 26 /* when subtracting the offset, the character could be a negative index */ ) mod 26 /* force between 0 and 25, inclusive */
output += outputIndex to character
else:
output += initial character
(set the output text field to the output)
Some other changes to your code:
Some other changes I made to your code that are required for this are:
Have a global array of all buttons, and a global array of all inputs. You kept overriding the value so there was only one reference at a time, so you weren't able to access the values in the encrypt and decrypt functions
Changed the order of creating the inputs (text fields), so that they are defined vertically, this is good for when we push to the array
Made the alphabet variable a string rather than an array, you can still do indexOf(), includes(), alphabet[i], etc. with a string and the definition looks cleaner
I want to check if an input is a valid bra measurement. In the US, bra sizes are written with an even number 28-48 and a letter A-I, AAA, AA, DD, DDD, HH or HHH. The EU, Japan and Australia use different numbers and patterns, ex. 90C C90 and DD6.
-I want to split the letters and digits, check that the letter is between A - I or AA, AAA, DD, DDD, HH or HHH, and that the number is 28 - 48 (even numbers only), 60-115 (increments of 5, so 65, 70, 75, etc.) or 6-28 even numbers only.
var input = $("#form_input").val("");
var bust = input.match(/[\d\.]+|\D+/g);
var vol = bust[0];
var band = bust[1];
I can write a long test condition:
if ((vol > 28 && vol < 48) && band == "AAA" || band == "AA" || band == "A" || band == "B" || etc.) { //some code
} else { error message" }```
How do I shorten this and do the above things using regex?
It is a bit of a long pattern with the alternatives, but you can easily adjust the ranges if something is missing or matches too much.
You can first check if the pattern matches using test. To get the band and the vol matches, one option is to extract either the digits or the uppercase chars from the match as there are matches for example for 90C and C90
^(?:(?:28|3[02468]|4[02468])(?:AA?|[BC]|D{1,4}|[E-I])|(?:[6-9][05]|1[01][05])(?:AA?|[BC]|DD?|[E-I])|[A-I](?:[6-9][05]|1[01][05])|(?:[68]|1[02468]|2[0246])(?:AA?|[BC]|DD?|[E-I]))$
Explanation
^ Start of string
(?: Non capture group for the alternatives
(?:28|3[02468]|4[02468]) Match from 28 - 48 in steps of 2
(?:AA?|[BC]|D{1,4}|[E-I]) Match AA, A, B, C, 1-4 times a D or a range E-I
| Or
(?:[6-9][05]|1[01][05]) Match from 60 - 115 insteps of 5
(?:AA?|[BC]|DD?|[E-I]) Match AA, A, B, C DD, D or a range E-I
| Or
[A-I](?:[6-9][05]|1[01][05]) Match a range A-I and a number 60 - 115 in steps of 5
| Or
(?:[68]|1[02468]|2[0246]) Match from 6 - 26 in steps of 2
(?:AA?|[BC]|DD?|[E-I]) Match AA, A, B, C, DD, D or a range E-I
) Close alternation
$ End of string
Regex demo
const pattern = /^(?:(?:28|3[02468]|4[02468])(?:AA?|[BC]|D{1,4}|[E-I])|(?:[6-9][05]|1[01][05])(?:AA?|[BC]|DD?|[E-I])|[A-I](?:[6-9][05]|1[01][05])|(?:[68]|1[02468]|2[0246])(?:AA?|[BC]|DD?|[E-I]))$/;
const str = `28A
28AA
30B
34AA
36DDDD
D70
I115
A70
H80
6AA
26I
`;
str.split('\n').forEach(s => {
if (pattern.test(s)) {
console.log(`Match: ${s}`);
let vol = s.match(/\d+/)[0];
let band = s.match(/[A-Z]+/)[0];
console.log(`vol: ${vol}`);
console.log(`band: ${band}`);
console.log("---------------------------------------");
}
})
^(((([0-4])(0|2|4|6|8))|(6|8))|(((6|7|8|9)(0|5))|(1[01][05])))((AAA)|(AA)|(DD)|(DDD)|(HH)|(HHH)|[A-I])$
Proof that all valid sizes match, while all 100_464 sample invalid sizes do not:
const validNumbers = Array
.from({ length: 22 }, (_, i) => 6 + (i * 2))
.concat(Array.from({ length: 12 }, (_, i) => 60 + (i * 5)));
const validLetters = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
'AAA', 'AA', 'DD', 'DDD', 'HH', 'HHH'
];
const validSizes = validNumbers.map((number) => validLetters
.map((letter) => number + letter))
.flat();
const invalidNumbers = Array
.from({ length: 1_000 }, (_, i) => i)
.filter((n) => !validNumbers.includes(n))
const invalidLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
.map((letter) => Array.from({ length: 4 }, (_, i) => letter.repeat(i + 1)))
.flat();
const invalidSizes = invalidNumbers.map((number) => invalidLetters
.map((letter) => number + letter))
.flat();
const regex = /^(((([0-4])(0|2|4|6|8))|(6|8))|(((6|7|8|9)(0|5))|(1[01][05])))((AAA)|(AA)|(DD)|(DDD)|(HH)|(HHH)|[A-I])$/;
const falsePositives = invalidSizes.filter((size) => regex.test(size));
console.log({ falsePositives });
console.log({ validSizes: validSizes.map((size) => ({ size, isValid: regex.test(size) })) });
I have a list of products that contains UOM in the product title. It needs automatically detect the UOM in the title by using Regex.
Expectations
Banana Yogurt 70ml returns ml
Fish Nuggets 200G returns g
Potato Wedges 200 G returns g
I have this function below
detectMetricUnit = (title) => {
let unit,
regex = new RegExp(/(?:\d)/mg),
measurement = title.match(regex) && title.match(regex)[0],
matches = measurement && title.split(measurement)[1];
if(matches) {
if(/millilitre|milliliter|ml/.test(matches.toLowerCase())){
unit = 'ml';
} else if(/litre|liter|l/.test(matches.toLowerCase())){
unit = 'l';
} else if (/kilogram|kg/.test(matches.toLowerCase())) {
unit = 'kg';
} else if (/gram|g/.test(matches.toLowerCase())) {
unit = 'g';
}
}
return unit;
}
However I have some problematic strings such as
Chocolate Drink 330ML X 24 matches 3 and return null UOM
which I am expecting to get ml.
Appreciate if someone could point out my mistake in my regex. How do I actually get the full integers and find the UOM attached next to it even with a space?
You may define a dictionary of possible UOMs you want to detect and then build a regex similar to
/(\d+(?:\.\d+)?)\s?(millilitre|milliliter|ml|litre|liter|l|kilogram|kg|gram|g)\b/i
See the regex demo. The (\d+(?:\.\d+)?) part will capture an integer or float value into Group 1, then \s? match an optional whitespace (change to \s* to match 0 or more whitespaces), and then (millilitre|milliliter|ml|litre|liter|l|kilogram|kg|gram|g)\b will capture UOM unit into Group 2 as a whole word (due to \b word boundary).
Here is the JS implementation to get the first UOM from string:
let strs = ['Banana Yogurt 70ml', 'Fish Nuggets 200G', 'Potato Wedges 200 G', 'Chocolate Drink 330ML X 24']
let dct = {millilitre: 'ml', milliliter: 'ml', ml: 'ml', litre:'l', liter: 'l', l: 'l', kilogram: 'kg', kg: 'kg', gram: 'g', g: 'g'}
detectMetricUnit = (title) => {
let unit, match, val,
regex = new RegExp("(\\d+(?:\\.\\d+)?)\\s?(" + Object.keys(dct).join("|") + ")\\b", "i");
match = title.match(regex);
if (match) {
val = match[1];
unit = dct[match[2].toLowerCase()]
}
return [val, unit];
}
strs.forEach(x => console.log(detectMetricUnit(x)) )
To get all of them, multiple occurrences:
let strs = ['Banana Yogurt 70ml and Fish Nuggets 200G', 'Potato Wedges 200 G and Chocolate Drink 330ML X 24']
let dct = {millilitre: 'ml', milliliter: 'ml', ml: 'ml', litre:'l', liter: 'l', l: 'l', kilogram: 'kg', kg: 'kg', gram: 'g', g: 'g'}
detectMetricUnit = (title) => {
let match, results = [],
regex = new RegExp("(\\d+(?:\\.\\d+)?)\\s?(" + Object.keys(dct).join("|") + ")\\b", "ig");
while (match=regex.exec(title)) {
results.push([ match[1], dct[match[2].toLowerCase()] ]);
}
return results;
}
strs.forEach(x => console.log(x, detectMetricUnit(x)) )
I have below Dygraph using the dygraphs package
library(dygraphs)
library(htmlwidgets)
library(zoo)
valueFormatter = "function formatValue(v) {
var suffixes = ['', 'K', 'M', 'G', 'T'];
if (v < 1000) return v;
var magnitude = Math.ceil(String(Math.floor(v)).length / 4-1);
if (magnitude > suffixes.length - 1)
magnitude = suffixes.length - 1;
return String(Math.round(v / Math.pow(10, magnitude * 3), 2)) +suffixes[magnitude]
}"
Data = zoo(matrix(c(10000, 1000000, 100000000, 50000), nc = 1), as.Date(c('2015-01-05', '2016-01-05', '2017-01-05', '2018-01-05'))); colnames(Data) = 'x'
dygraph(Data, main = "") %>% dySeries(c("x")) %>%
dyAxis("y", axisLabelFormatter = JS(valueFormatter),
valueFormatter = JS(valueFormatter),
rangePad = 20)
However in the label of Y-axis I want to bring Thousand seperator for the tick values e.g. instead of 30000K I want to have 3,0000K. Is there any way to achieve this.
Convert your number 30000K into thousand separated value 30,000K.
Regex to place comma after each 3rd digit
var regex = /\B(?=(\d{3})+(?!\d))/g;
var number = "200";
var thousandSeprator = "20000";
console.log(number.replace(regex,',')); // 200
console.log(thousandSeprator.replace(regex,',')); // 20,000
Updated valueFormatter function
valueFormatter = "function formatValue(v) {
var suffixes = ['', 'K', 'M', 'G', 'T'];
var regex = /\B(?=(\d{3})+(?!\d))/g;
if (v < 1000) return v.toString().replace(regex,',');
var magnitude = Math.ceil(String(Math.floor(v)).length / 4-1);
if (magnitude > suffixes.length - 1)
magnitude = suffixes.length - 1;
return v.toString().replace(regex,',') +suffixes[magnitude]
}"
So I'm currently making a bit of script that will take in a value between 0 and 3999 and pump out the roman numerals for the number. For some reason when calling my 2 dimensional array, I wind up getting undefined
function romanConverter() {
var romanOutput;
var enteredNum = prompt('What number would you like converted between 1 and 3999?');
var romanNum = [
['', 'M', 'MM', 'MMM'], // Thousands place
['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'], // Hundreds place
['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'], // These are for the tens place
['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'] // Ones place
];
if (parseInt(enteredNum) > 3999) {
alert("I'm sorry that's not a valid entry");
enteredNum = prompt('What number would you like converted between 1 and 3999?');
} else {
while (enteredNum.length < 4) {
enteredNum = '0' + enteredNum;
}
for (var i = 0; i <= enteredNum.length; i += 1) {
var currentNum = parseInt(enteredNum.charAt(i));
romanOutput += romanNum[i][currentNum];
}
}
document.write(romanOutput);
}
romanConverter();
I always get the TypeError: romanNum[i] is undefined I'm really stuck and could use some help.
This is one of those sneaky little errors that always makes you bang your head against a wall for a while.
Try changing your final for loop to the following:
for (var i = 0; i < enteredNum.length; i += 1) { // etc...
You want strictly less than - not less than or equal to. Otherwise, you'll end up evaluating something like "05".charAt(2);
To prevent its output from being undefined followed by the converted roman numeral, you will also want to change the line that says
var romanOutput;
To
var romanOutput = "";