A regex for an application version - javascript

I would like to write regex for matching an application version in javascript using pattern:
(0-255).(0-255)[.(0-65535).(0-65535)]
Here is my result:
^(?:(\d+)\.){1}(?:(\d+)\.){1}(?:(\d+)\.)?(\d+)?$
But it allows strings with dot in the end (like '111.222.333.') and doesn't limit number of digits.
Any help?
Update
This pattern is better:
(0-255).(0-255)[.(0-65535)][.(0-65535)]
The result is:
^(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])[.](?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])(?:(?:[.](?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){1})?(?:(?:[.](?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){1})?$

I think the best solution for you would be to split by . and check each part:
function isInBounds(value, min, max) {
return !isNaN(value) && value >= min && value <= max;
}
function checkVersion(version) {
var parts = version.split(".");
switch (parts.length) {
case 4:
case 3:
for (var i = 2; i < parts.length; i++) {
if (!isInBounds(parseInt(parts[i], 10), 0, 65535)) {
return false;
}
}
// fallthrough
case 2:
for (var i = 0; i < 2; i++) {
if (!isInBounds(parseInt(parts[i], 10), 0, 255)) {
return false;
}
}
break;
default:
return false;
}
return true;
}
console.log(checkVersion("foo")); // false
console.log(checkVersion("foo.bar")); // false
console.log(checkVersion("foo.bar.foo")); // false
console.log(checkVersion("foo.bar.foo.bar")); // false
console.log(checkVersion("256")); // false
console.log(checkVersion("256.256")); // false
console.log(checkVersion("256.256.65536")); // false
console.log(checkVersion("256.256.65536.65536")); // false
console.log(checkVersion("42")); // false
console.log(checkVersion("42.42")); // true
console.log(checkVersion("42.42.42")); // true
console.log(checkVersion("42.42.42.42")); // true
See on jsFiddle
Regex is probably not the way to go, since it does not handle ranges very good. Just for the challenge, here is the one you would need (RegexForRange helped a lot ;)):
^(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])[.](?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])(?:(?:[.](?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){1,2})?$
Visualization by Debuggex

Related

Each digit differs from the next one by 1

Script has to work this way:
isJumping(9) === 'JUMPING'
This is a one digit number
isJumping(79) === 'NOT JUMPING'
Neighboring digits do not differ by 1
isJumping(23454) === 'JUMPING'
Neighboring digits differ by 1
I have:
function isJumping(number) {
let str = number.toString();
for (let i = 1; i < str.length; i++) {
if (Math.abs(str[i+1]) - Math.abs(str[i]) == 1){
return 'JUMPING';
}
}
return 'NOT JUMPING';
}
console.log(isJumping(345));
Help please, where is mistake?
Loop over the characters and early return with "NOT JUMPING" if the condition is violated & if the condition is never violated return "JUMPING".
function isJumping(num) {
const strNum = String(Math.abs(num));
for (let i = 0; i < strNum.length - 1; i++) {
if (Math.abs(strNum[i] - strNum[i + 1]) > 1) {
// replace `> 1` with `!== 1`, if diff 0 is not valid!
return "NOT JUMPING";
}
}
return "JUMPING";
}
console.log(isJumping(9));
console.log(isJumping(79));
console.log(isJumping(23454));
There are a couple of issues:
You're not handling single digits.
You're returning too early. You're returning the first time you see a difference of 1 between digits, but you don't know that subsequent differences will also be 1.
You're not checking the difference between the first and second digits, and you're going past the end of the string.
You're using Math.abs as a means of converting digits to numbers.
Instead (see comments):
function isJumping(number) {
let str = number.toString();
for (let i = 1; i < str.length; i++) {
// Convert to number, do the difference, then
// use Math.abs to make -1 into 1 if necessary
if (Math.abs(+str[i] - str[i-1]) !== 1) {
// Found a difference != 1, so we're not jumping
return "NOT JUMPING";
}
}
// Never found a difference != 1, so we're jumping
return "JUMPING";
}
console.log(isJumping(345)); // JUMPING
console.log(isJumping(9)); // JUMPING
console.log(isJumping(79)); // NOT JUMPING
console.log(isJumping(23454)); // JUMPING
In that, I use +str[i] to convert str[i] to number and implicitly convert str[i-1] to number via the - operator, but there are lots of ways to convert strings to numbers (I list them here), pick the one that makes sense for your use case.
You might also need to allow for negative numbers (isJumping(-23)).
A clumsy way would be if (Math.abs(Math.abs(str[i+1]) - Math.abs(str[i])) == 1). Right now you are using Math.abs() to convert digits to numbers. Also, indexing is off, you start from 1, which is good, but then you should compare [i] and [i-1]. And the usual mismatch: you can say "JUMPING", only at the end. So you should check for !==1, and return "NOT JUMPING" inside the loop, and "JUMPING" after. That would handle the 1-digit case too.
It's a more readable practice to use parseInt() for making a number from a digit, otherwise the implementation of the comment:
function isJumping(number) {
let str = number.toString();
for (let i = 1; i < str.length; i++) {
if (Math.abs(parseInt(str[i-1]) - parseInt(str[i])) !== 1){
return 'NOT JUMPING';
}
}
return 'JUMPING';
}
console.log(isJumping(345));
console.log(isJumping(3));
console.log(isJumping(79));
You just need to check your single digit case, and then see if all the digits vary by just 1
function isJumping(number) {
let str = number.toString();
if(str.length == 1)
return 'JUMPING'
const allByOne = str.substring(1).split('').every( (x,i) => {
var prev = str[i];
return Math.abs( +x - +prev) == 1
})
return allByOne ? 'JUMPING' : 'NOT JUMPING';
}
console.log(isJumping(9));
console.log(isJumping(79));
console.log(isJumping(23454));
A vaguely functional approach... The find gets position of the first character pair where the gap is more than one. The .filter deals with negatives (and other extraneous characters) by ignoring them.
// default b to a, so that last digit case, where b===undefined, gives true
const gapIsMoreThanOne = (a,b=a) => ( Math.abs(a-b)>1);
const isDigit = n => /[0-9]/.test(n);
const isJumping = n => n.toString()
.split("")
.filter(isDigit)
.find((x,i,arr)=>gapIsMoreThanOne(x,arr[i+1]))
=== undefined
? "JUMPING" : "NOT JUMPING"
;
console.log(isJumping(1)); // JUMPING
console.log(isJumping(12)); // JUMPING
console.log(isJumping(13)); // NOT JUMPING
console.log(isJumping(21)); // JUMPING
console.log(isJumping(21234565)); // JUPING
console.log(isJumping(-21234568)); // NOT JUMPING
console.log(isJumping("313ADVD")); // NOT JUMPING
PS: To me "JUMPING" implies that there is a gap greater than one, not that there isn't: but I've gone with how it is in the question.

JS chaining functions

I'm writing a function that should work like this:
checker(3).equals(3) // true
checker(3).not().equals(3) // false
checker(3).not().equals(4) // true
checker(3).not().not().equals(4) // false
The code I came up with:
function checker(num) {
let number = num
return {
not() {
number = !number
return this
},
equals(nmb) {
return number === nmb
}
}
}
I can't wrap my head around what should not() do so as to make checker(num) work as it is supposed to.
You can add another boolean property that changes how equals works depending on it's value.
function checker(num) {
let number = num
let not = false
return {
not() {
not = !not
return this
},
equals(nmb) {
return not ? number !== nmb : number === nmb
}
}
}
console.log(checker(3).equals(3)) // true
console.log(checker(3).not().equals(3)) // false
console.log(checker(3).not().equals(4)) // true
console.log(checker(3).not().not().equals(4)) // false
Maybe somthing like this:
function checker(num) {
let number = num
let beTrue = true;
return {
not() {
beTrue = !beTrue;
return this
},
equals(nmb) {
return (number === nmb) === beTrue;
}
}
}
It seems to fullfil your requirements. Hope it helps
An ES6 approach
const checker=(number, negate)=>{
const neg = negate || false;
return {
not(){
return checker(number, !neg);
},
equals(number2){
if (neg) return number != number2;
return number == number2;
}
}
}
what should not() do so as to make checker(num) work as it is supposed to.
not could return a new checker.
I think i would let the notfunction control the operator, something like
function checker(num) {
let operator = 'equals';
let number = num
return {
not() {
if(operator==='equals')
operator = 'not equals';
else
operator = 'equals';
return this
},
equals(nmb) {
if(operator==='equals')
return number === nmb
else
return number !== nmb
}
}
}
just using a string as operator for clarity, a proberly better solution could be to use a boolean or number value

Eloquent JavaScript, Sequence Interface

I am working my way through Eloquent JavaScript, and I am having questions about problem #6.3 (second edition).
Here is the problem:
Design an interface that abstracts iteration over a collection of
values. An object that provides this interface represents a sequence,
and the interface must somehow make it possible for code that uses
such an object to iterate over the sequence, looking at the element
values it is made up of and having some way to find out when the end
of the sequence is reached.
When you have specified your interface, try to write a function
logFive that takes a sequence object and calls console.log on its
first five elements—or fewer, if the sequence has fewer than five
elements.
Then implement an object type ArraySeq that wraps an array and allows
iteration over the array using the interface you designed. Implement
another object type RangeSeq that iterates over a range of integers
(taking from and to arguments to its constructor) instead.
I wrote this solution:
function ArraySeq(collection) {
this.values = collection;
}
ArraySeq.prototype.iterate = function(start, end, action) {
var n = Math.min(this.values.length, end);
for (var i = start; i < n; i++) {
action(this.values[i]);
}
};
function RangeSeq(from, to) {
var array = [];
for (var i = from; i <= to; i++)
array.push(i);
ArraySeq.call(this, array);
}
RangeSeq.prototype = Object.create(ArraySeq.prototype);
function logFive(sequenceObject) {
sequenceObject.iterate(0, 5, console.log);
}
//This code to test how the solution works was provided by the author
logFive(new ArraySeq([1, 2]));
// → 1
// → 2
logFive(new RangeSeq(100, 1000));
// → 100
// → 101
// → 102
// → 103
// → 104
The solutions by the author are different, here are both of them:
// I am going to use a system where a sequence object has two methods:
//
// * next(), which returns a boolean indicating whether there are more
// elements in the sequence, and moves it forward to the next
// element when there are.
//
// * current(), which returns the current element, and should only be
// called after next() has returned true at least once.
function logFive(sequence) {
for (var i = 0; i < 5; i++) {
if (!sequence.next())
break;
console.log(sequence.current());
}
}
function ArraySeq(array) {
this.pos = -1;
this.array = array;
}
ArraySeq.prototype.next = function() {
if (this.pos >= this.array.length-1)
return false;
this.pos++;
return true;
};
ArraySeq.prototype.current = function() {
return this.array[this.pos];
};
function RangeSeq(from, to) {
this.pos = from - 1;
this.to = to;
}
RangeSeq.prototype.next = function() {
if (this.pos >= this.to)
return false;
this.pos++;
return true;
};
RangeSeq.prototype.current = function() {
return this.pos;
};
logFive(new ArraySeq([1, 2]));
// → 1
// → 2
logFive(new RangeSeq(100, 1000));
// → 100
// → 101
// → 102
// → 103
// → 104
// This alternative approach represents the empty sequence as null,
// and gives non-empty sequences two methods:
//
// * head() returns the element at the start of the sequence.
//
// * rest() returns the rest of the sequence, or null if there are no
// elemements left.
//
// Because a JavaScript constructor can not return null, we add a make
// function to constructors of this type of sequence, which constructs
// a sequence, or returns null if the resulting sequence would be
// empty.
function logFive2(sequence) {
for (var i = 0; i < 5 && sequence != null; i++) {
console.log(sequence.head());
sequence = sequence.rest();
}
}
function ArraySeq2(array, offset) {
this.array = array;
this.offset = offset;
}
ArraySeq2.prototype.rest = function() {
return ArraySeq2.make(this.array, this.offset + 1);
};
ArraySeq2.prototype.head = function() {
return this.array[this.offset];
};
ArraySeq2.make = function(array, offset) {
if (offset == null) offset = 0;
if (offset >= array.length)
return null;
else
return new ArraySeq2(array, offset);
};
function RangeSeq2(from, to) {
this.from = from;
this.to = to;
}
RangeSeq2.prototype.rest = function() {
return RangeSeq2.make(this.from + 1, this.to);
};
RangeSeq2.prototype.head = function() {
return this.from;
};
RangeSeq2.make = function(from, to) {
if (from > to)
return null;
else
return new RangeSeq2(from, to);
};
logFive2(ArraySeq2.make([1, 2]));
// → 1
// → 2
logFive2(RangeSeq2.make(100, 1000));
// → 100
// → 101
// → 102
// → 103
// → 104
My questions:
My solution produces exactly the same results as the author's solutions. I tested all three with various test cases. I did utilize the material of the chapter when writing my solution (the chapter talks about OOP techniques in JavaScript, inheritance and such). Given all this, is my solution wrong or not?
Why are his solutions written the way they are written? I mean, is there something wrong with my approach? Not abstract enough? Is it a bad thing that my method accepts arguments? His methods do not require arguments. Are there any drawbacks to my solution comparing to his? (I must confess, I barely understood the problem, so my solution probably reflects the level of my understanding of the problem.)
Thank you!
Your solution is not correct, because the whole point of the iterator protocol is to iterate lazily, so that RangeSeq(1, 10000000) doesn't fill up memory with all elements if we only need five.
The author's code is ok, but unnecessarily verbose. Iterator protocols are usually implemented like this:
an Iterable is an object that has the iterator() method
Iterable.iterator() returns an Iterator object
Iterator provides the method .next which returns a pair (done, value). Alternatively, .next can throw an exception when called on an exhausted Iterator.
Examples:
function RangeSeq(from, to) {
this.iterator = function() {
var i = from;
return {
next: function() { return [i >= to, i++] }
}
}
}
function ArraySeq(ary) {
this.iterator = function() {
var i = 0;
return {
next: function() { return [ i >= ary.length, ary[i++]] }
}
}
}
function logN(seq, n) {
var it = seq.iterator();
while (n--) {
let [done, value] = it.next();
if (done) break;
console.log(value)
}
}
logN(new RangeSeq(100, 100000000), 5);
console.log('--------------');
logN(new ArraySeq([11,22,33,44,55,66,77,88,99]), 5);
That said, modern JS engines have this kind of stuff built-in, so the Range example can be written much simpler as
function RangeSeq(from, to) {
this[Symbol.iterator] = function*() {
for (let i = from; i < to; i++)
yield i;
}
}
n = 0;
rs = new RangeSeq(100, 10000000);
for (let x of rs) {
if (n++ >= 5) break;
console.log(x)
}

Letter grade value to number value then averaged

I'm writing a script for my schools web form for calculating credits and gpa values. When I run the input for letter grades I convert to uppercase and then check with if/else statements for A-F options and convert the value of the input to a number value which is then passed to another function to average the total for the column inputs which is then returned back to a letter grade. The issue i'm having is that when i use my keyup jquery function it returns a NaN value instead of a letter... HELP
function CalcGPA(a,b,c,d,e,f,g,h,i,j){
var initial=a+b+c+d+e+f+g+h+i+j;
var total=initial/10;
return total;}
function Convert(a){
var b=a.value.toUpperCase();
if(b.value="A")
{
b=4.0;
return b;
}
else if(b.value="A-")
{
b=3.67;
return b;
}
else if(b.value="B+")
{
b=3.33;
return b;
}
else if(b.value="B")
{
b=3.0;
return b;
}
else if(b.value="B-")
{
b=2.67;
return b;
}
else if(b.value="C+")
{
b=2.33;
return b;
}
else if(b.value="C"){
b=2.0;
return b;
}
else if(b.value="C-")
{
b=1.7;
return b;
}
else if(b.value="D")
{
b=1.0;
return b;
}
else {
b=0.0;}
return b;}
function toLetter(a){
if(a<=4||a>=3.68)
{
a="A";
}
else if(a<=3.67 || a>=3.34)
{
a="A-";
}
else if(a<=3.33 || a>=3.1)
{
a="B+";
}
else if(a<=3.0 || a>=2.68)
{
a="B";
}
else if(a<=2.67 || a>=2.34)
{
a="B-";
}
else if(a<=2.33 || a>=2.1)
{
a="C+";
}
else if(a<=2.0 || a>=1.8)
{
a="C";
}
else if(a<=1.7 || a>=1.4)
{
a="C-";
}
else if(a<=1.3 || a>=1.1)
{
a="D+";
}
else if (a=1.0)
{
a="D";
}
else {
a="F";}
return a;}
You should use parseInt or parseFloat in your toLetter function I suppose, and in your if statements, use two equal signs instead of one in order to compare:
function toLetter(a) {
var a = parseInt(a); //or parseFloat if you need decimals.
//rest of your code...
I strongly recommend a little refactoring here, for instance, instead of declaring and then returning, just return:
else if(b.value == "B+")
{
return 3.33;
}
Another improvement, move your statements to a switch:
switch(b.value) {
case "B+" :
return 3.33;
case "B" :
return 3.0;
}
And, one thing I would do, instead of conditions or cases, use a map:
var scoresForValues = {
"A": 4.0,
"A-": 3.67,
"B+": 3.33 //Keep filling until you get all your values.
};
And then your Convert function would be really simple and short:
function Convert(a) {
var b = a.value.toUpperCase();
return scoresForValues[b];
}
Use parseInt for integers or if you want to use decimal values, use parseFloat.
There are a couple of things here:
First, you are trying to compare using "=", and that can't work. You need to use "==". For example, else if(b.value="C+") should be else if(b.value=="C+")
Second, you've made b a string when you declared it and assigned the input to it:
var b=a.value.toUpperCase();
So, don't assign the numerical value to it and return it, just return the numerical value. Same for your a variable in toLetter. Just return the letter value and you can bail out of all your elseif lines as soon as you have a match.
So, instead of a="B-";, just return "B-"
finally, you should probably use switch case instead of your pile of if... else if. NOTE: you don't need break; after return 3 (for instance) because a return bails you out of the function. If you're using case switch for anything not causing a return, leaving out break; will cause you a lot of hurt.
function Convert(a){
var b=a.value.toUpperCase();
switch (b) {
case "A":
return 4.0;
break;
case "A-":
return 3.67;
break;
case "B+":
return 3.33;
break;
case "B":
return 3;
break;
case "B-":
return 2.67;
break;
case "C+":
return 2.33;
break;
case "C":
return 2;
break;
case "C-":
return 1.67;
break;
case "D":
return 1;
break;
default:
return 0;
}

Javascript square function

I want to write a function that checks if the given number has a certain order.
The second number has to be the square of the previous number.
The first number can only be 0 - 9.
So for example 2439 would return 'true' because 4 is the square of 2 and 9 is the square of 3.
39416 would also give 'true', and for example 1624 would return 'false'.
I don't really have an idea how to do this. It should be a recursive function but an example of how to do it without recursion would be helpfull too.
I would try something like this:
function isOrdered(input){
var position = 0;
while(position<input.length-2)
{
var currentFirstNumber = parseInt(input[position]);
if(currentFirstNumber<=2) {
if (Math.sqrt(parseInt(input[position + 1])) !== currentFirstNumber)
return false;
else
position+=2;
}
if(currentFirstNumber>=4 && currentFirstNumber<=9)
{
var squared = input.substring(position+1,position+3);
if(Math.sqrt(parseInt(squared))!==currentFirstNumber)
return false;
else
position=position+3;
}
}
return true;
}
console.log(isOrdered("2439")); // outputs true
console.log(isOrdered("39416")); // outputs true
console.log(isOrdered("1624")); // outputs false
I pass the number to the function as a string.
Take a look at this recursive function
function detectOrder(input)
{
var input = input.toString()
var first = input.substr(0,1);
var power = Math.pow(parseInt(first), 2);
var powerLength = power.toString().length;
if ( parseInt(input.substr(1, powerLength)) == power )
{
if (input.length <= 1+powerLength)
{
return true;
}
else
{
return detectOrder(input.substr(1+powerLength));
}
}
else
{
return false;
}
}
As mention in the comment section, OP said that the 'firsts' are limited to 0..9. So the easiest way to accomplish this is by going through the power function instead of the square root function.
UPDATE: Sorry, you asked for JavaScript code. Be careful with the FIRST CALL. if you manually pass to the function the last position, it will return true.
function verification(number, position){
var str = String(number); // Cast number to String
if(str.length > position){ // Verify valid position
var value = str.substr(position, 1); // take the 'first' value
var pow = Math.pow(value, 2); // Calculate the power
// Verify if the next value is equivalent to the power
if(str.indexOf(pow, position) == position + 1){
// Recursive call to verify the next position
return verification(number, position + String(pow).length + 1);
} else {
// If not equivalent, you found an inconsistency.
return false;
}
// If you ran it until you reached the last position, you're good to go.
}else if(str.length == position){
return true;
}
}
console.log(verification(39416, 0)); // True
console.log(verification(39415, 0)); // True
console.log(verification(981524, 0)); // false
console.log(verification(981525, 0)); // true
console.log(verification(98525, 0)); // false

Categories

Resources