Match IP with a range stored in an array - javascript

I am returning an IP and then comparing it from a range.
I am just looking at the US IP and if it falls in a range I am displaying a messge. I am getting the IP correctly but when I am trying to match it with the range I am getting a syntax error of unexpected range. How should I resolve this?
here is how my code looks like
$.getJSON("http://jsonip.com", function (data) {
var x = data.ip;
$('body').html(x);
var usRange = [3.0.0.0, 222.229.21.255];
if (x >= usRange[0] && x <= usRange[1]) {
alert('US');
} else alert('outside US');
});
Here is my fiddle

What the error means, you can't assign number with 4 decimal dots.
var usRange = [3.0.0.0, 222.229.21.255]; //Not possible
FYI: The IP address returned from the json is string, not a number.
So you approach won't work. Here check my approach,
1) Split the ip address based on . returned by $.getJSON
var x = data.ip.split('.'); // returns ["122", "164", "17", "211"]
2) Instead of using usRange array just do it with simple comparison operations.
3) You cannot compare strings with numbers, so convert those strings to numbers like
+x[0] //convert "122" to 122
Finally,
$.getJSON("http://jsonip.com", function (data) {
var x = data.ip.split('.');
if (+x[0] >= 3 && +x[0] <= 222) {
if (+x[1] >= 0 && +x[1] <= 229) {
if (+x[2] >= 0 && +x[2] <= 21) {
if (+x[3] >= 0 && +x[3] <= 255) {
alert("Within range");
}
}
}
} else {
alert("Is not within range");
}
});
JSFiddle

I decided to rewrite my answer here for the sake of a better method. The first method actually converts the IP addresses to integers using bit shifting and then compares the integer values. I've left the second answer as an option but I prefer the first.
Neither of these functions will validate your IP addresses!
Method 1: Bit Shifting
/**
* Checks if an IP address is within range of 2 other IP addresses
* #param {String} ip IP to validate
* #param {String} lowerBound The lower bound of the range
* #param {String} upperBound The upper bound of the range
* #return {Boolean} True or false
*/
function isWithinRange(ip, lowerBound, upperBound) {
// Put all IPs into one array for iterating and split all into their own
// array of segments
var ips = [ip.split('.'), lowerBound.split('.'), upperBound.split('.')];
// Convert all IPs to ints
for(var i = 0; i < ips.length; i++) {
// Typecast all segments of all ips to ints
for(var j = 0; j < ips[i].length; j++) {
ips[i][j] = parseInt(ips[i][j]);
}
// Bit shift each segment to make it easier to compare
ips[i] =
(ips[i][0] << 24) +
(ips[i][1] << 16) +
(ips[i][2] << 8) +
(ips[i][3]);
}
// Do comparisons
if(ips[0] >= ips[1] && ips[0] <= ips[2]) return true;
return false;
}
Method 2: Plain 'Ol Logic Mess
/**
* Checks if an IP address is within range of 2 other IP addresses
* #param {String} ip IP to validate
* #param {String} lowerBound The lower bound of the range
* #param {String} upperBound The upper bound of the range
* #return {Boolean} True or false
*/
function isWithinRange(ip, lowerBound, upperBound) {
// Save us some processing time if the IP equals either the lower bound or
// upper bound
if (ip === lowerBound || ip === upperBound) return true;
// Split IPs into arrays for iterations below. Use same variables since
// we won't need them as strings anymore and because someone will complain
// about wasting memory.
ip = ip.split('.');
lowerBound = lowerBound.split('.');
upperBound = upperBound.split('.');
// A nice, classic for loop iterating over each segment in the IP address
for (var i = 0; i < 4; i++) {
// We need those segments to be converted to ints or our comparisons
// will not work!
ip[i] = parseInt(ip[i]);
lowerBound[i] = parseInt(lowerBound[i]);
upperBound[i] = parseInt(upperBound[i]);
// If this is our first iteration, just make sure the first segment
// falls within or equal to the range values
if (i === 0) {
if (ip[i] < lowerBound[i] || ip[i] > upperBound[i]) {
return false;
}
}
// If the last segment was equal to the corresponding lower bound
// segment, make sure that the current segment is greater
if (ip[i - 1] === lowerBound[i - 1]) {
if (ip[i] < lowerBound[i]) return false;
}
// If the last segment was equal to the corresponding upper bound
// segment, make sure that the current segment is less than
if (ip[i - 1] === upperBound[i - 1]) {
if (ip[i] > upperBound[i]) return false;
}
}
return true;
}

Related

How do multiple else if statements work in Javascript? [duplicate]

I want to check if a value is in an accepted range. If yes, to do something; otherwise, something else.
The range is 0.001-0.009. I know how to use multiple if to check this, but I want to know if there is any way to check it in a single if statement.
You're asking a question about numeric comparisons, so regular expressions really have nothing to do with the issue. You don't need "multiple if" statements to do it, either:
if (x >= 0.001 && x <= 0.009) {
// something
}
You could write yourself a "between()" function:
function between(x, min, max) {
return x >= min && x <= max;
}
// ...
if (between(x, 0.001, 0.009)) {
// something
}
Here is an option with only a single comparison.
// return true if in range, otherwise false
function inRange(x, min, max) {
return ((x-min)*(x-max) <= 0);
}
console.log(inRange(5, 1, 10)); // true
console.log(inRange(-5, 1, 10)); // false
console.log(inRange(20, 1, 10)); // false
If you must use a regexp (and really, you shouldn't!) this will work:
/^0\.00([1-8]\d*|90*)$/
should work, i.e.
^ nothing before,
followed by 0.00 (nb: backslash escape for the . character)
followed by 1 through 8, and any number of additional digits
or 9, followed by any number of zeroes
$: followed by nothing else
If you're already using lodash, you could use the inRange() function:
https://lodash.com/docs/4.17.15#inRange
_.inRange(3, 2, 4);
// => true
_.inRange(4, 8);
// => true
_.inRange(4, 2);
// => false
_.inRange(2, 2);
// => false
_.inRange(1.2, 2);
// => true
_.inRange(5.2, 4);
// => false
_.inRange(-3, -2, -6);
// => true
I like Pointy's between function so I wrote a similar one that worked well for my scenario.
/**
* Checks if an integer is within ±x another integer.
* #param {int} op - The integer in question
* #param {int} target - The integer to compare to
* #param {int} range - the range ±
*/
function nearInt(op, target, range) {
return op < target + range && op > target - range;
}
so if you wanted to see if x was within ±10 of y:
var x = 100;
var y = 115;
nearInt(x,y,10) = false
I'm using it for detecting a long-press on mobile:
//make sure they haven't moved too much during long press.
if (!nearInt(Last.x,Start.x,5) || !nearInt(Last.y, Start.y,5)) clearTimeout(t);
If you want your code to pick a specific range of digits, be sure to use the && operator instead of the ||.
if (x >= 4 && x <= 9) {
// do something
} else {
// do something else
}
// be sure not to do this
if (x >= 4 || x <= 9) {
// do something
} else {
// do something else
}
You must want to determine the lower and upper bound before writing the condition
function between(value,first,last) {
let lower = Math.min(first,last) , upper = Math.max(first,last);
return value >= lower && value <= upper ;
}
const inRange = (num, num1, num2) => Math.min(num1, num2) <= num && Math.max(num1, num2) >= num;
Could be like this if you want to make inRange inclusive and not depend on order of range numbers (num1, num2).

Leetcode Test case Fail

I am trying to solve a leetcode [problem][1]. The question says to search in a 2d array. While my code passes for most of the test cases it fails for a particular test case.
/**
* #param {number[][]} matrix
* #param {number} target
* #return {boolean}
*/
/**
* #param {number[][]} matrix
* #param {number} target
* #return {boolean}
*/
var searchMatrix = function(matrix, target) {
let i = 0 ;
let j = matrix.length ;
while(i <= matrix.length - 1 && j >= 0){
if(matrix[i][j] == target){
return true
}
if(matrix[i][j] > target){
j--;
} else {
i++;
}
}
return false
};
searchMatrix([1,3],3)
Above solution gives false whereas the correct answer should be true. What's wrong here ? Cant find out !
[1]: https://leetcode.com/problems/search-a-2d-matrix/
This question has two variations one on Leetcode and another on Geeks for Geeks.
Your solution above would work for GFG platform but would fail on leetcode.
Why?
Variation in both question lies how elements are arranged. In leetcode question first element of every row will be greater than last element of the previous row, which is not in the case of GFG. On GFG you will have row and column wise sorted matrix.
Your solution would pass the GFG test cases but would fail on leetcode. Hence one of the optimized solutions that you can go with is to use its question property and imagine it as a one dimensional array.
function searchMatrix(matrix, target){
let numberOfRows = matrix.length
let numberOFColums = matrix[0].length
let upperBoundOfMatrix = numberOfRows * numberOFColums - 1;
let start = 0
while(start <= upperBoundOfMatrix){
let mid = Math.floor(start + (upperBoundOfMatrix - start)/2);
let row = Math.floor(mid/numberOFColums);
let column = Math.floor(mid % numberOFColums);
if(matrix[row][column] == target){
return true
}
if(matrix[row][column] > target){
upperBoundOfMatrix = mid - 1;
} else {
start = mid + 1;
}
}
return false
}

How to get Row & Column values from A1Notation

Writing scripts for Google Spreadsheets can be difficult sometimes, because the Google Spreadsheet methods that take row and column numbers use 1-based indexing, while Javascript arrays use 0-based.
In this example, cell A2 has a row == 2 and column == 1. The SpreadsheetApp methods reverse column & row from those in A1Notation, so these two ranges are equivalent:
var range1 = sheet.getRange("A2");
var range2 = sheet.getRange(2, 1);
Once I read the contents of a sheet into an array, things are different again.
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var data = sheet.getDataRange().getValues();
After that, the value that was in cell A2 in my spreadsheet is in data[1][0]. The row & column are in the same order as the SpreadsheetApp API, but each is 1 less.
The answers to lots of questions here (example) have boiled down to mismatching of these different forms of indexing. Code that is full of row+1 and col-1 statements can be hard to debug.
Finally: If I know the reference for a cell in A1Notation, say AZ342, how can I find out the correct index values correspond to that cell in the 2D array, data, obtained from the full Data Range?
Obviously, you can just be very careful about keeping track of the places where you're using either type of indexing, and you'll be fine.
But it would be easier to do something like this:
var importantCell = "AZ342";
var cellIndexConverted = cellA1ToIndex( importantCell );
var data[cellIndexConverted.row][cellIndexConverted.col] = "Some new value";
ConvertA1.gs
Here are three helper functions to simplify conversion from A1Notation.
These helper functions are also available as a gist.
/**
* Convert a cell reference from A1Notation to 0-based indices (for arrays)
* or 1-based indices (for Spreadsheet Service methods).
*
* #param {String} cellA1 Cell reference to be converted.
* #param {Number} index (optional, default 0) Indicate 0 or 1 indexing
*
* #return {object} {row,col}, both 0-based array indices.
*
* #throws Error if invalid parameter
*/
function cellA1ToIndex( cellA1, index ) {
// Ensure index is (default) 0 or 1, no other values accepted.
index = index || 0;
index = (index == 0) ? 0 : 1;
// Use regex match to find column & row references.
// Must start with letters, end with numbers.
// This regex still allows induhviduals to provide illegal strings like "AB.#%123"
var match = cellA1.match(/(^[A-Z]+)|([0-9]+$)/gm);
if (match.length != 2) throw new Error( "Invalid cell reference" );
var colA1 = match[0];
var rowA1 = match[1];
return { row: rowA1ToIndex( rowA1, index ),
col: colA1ToIndex( colA1, index ) };
}
/**
* Return a 0-based array index corresponding to a spreadsheet column
* label, as in A1 notation.
*
* #param {String} colA1 Column label to be converted.
*
* #return {Number} 0-based array index.
* #param {Number} index (optional, default 0) Indicate 0 or 1 indexing
*
* #throws Error if invalid parameter
*/
function colA1ToIndex( colA1, index ) {
if (typeof colA1 !== 'string' || colA1.length > 2)
throw new Error( "Expected column label." );
// Ensure index is (default) 0 or 1, no other values accepted.
index = index || 0;
index = (index == 0) ? 0 : 1;
var A = "A".charCodeAt(0);
var number = colA1.charCodeAt(colA1.length-1) - A;
if (colA1.length == 2) {
number += 26 * (colA1.charCodeAt(0) - A + 1);
}
return number + index;
}
/**
* Return a 0-based array index corresponding to a spreadsheet row
* number, as in A1 notation. Almost pointless, really, but maintains
* symmetry with colA1ToIndex().
*
* #param {Number} rowA1 Row number to be converted.
* #param {Number} index (optional, default 0) Indicate 0 or 1 indexing
*
* #return {Number} 0-based array index.
*/
function rowA1ToIndex( rowA1, index ) {
// Ensure index is (default) 0 or 1, no other values accepted.
index = index || 0;
index = (index == 0) ? 0 : 1;
return rowA1 - 1 + index;
}
I wanted to post another solution since I usually think about converting strings using a loop rather than checking its length.
function convertSheetNotation(a1_notation) {
const match = a1_notation.match(/(^[A-Z]+)|([0-9]+$)/gm);
if (match.length !== 2) {
throw new Error('The given value was invalid. Cannot convert Google Sheet A1 notation to indexes');
}
const column_notation = match[0];
const row_notation = match[1];
const column = convertColumnNotationToIndex(column_notation);
const row = convertRowNotationToIndex(row_notation);
return [row, column];
}
function convertColumnNotationToIndex(a1_column_notation) {
const A = 'A'.charCodeAt(0);
let output = 0;
for (let i = 0; i < a1_column_notation.length; i++) {
const next_char = a1_column_notation.charAt(i);
const column_shift = 26 * i;
output += column_shift + (next_char.charCodeAt(0) - A);
}
return output;
}
function convertRowNotationToIndex(a1_row_notation) {
const num = parseInt(a1_row_notation, 10);
if (Number.isNaN(num)) {
throw new Error('The given value was not a valid number. Cannot convert Google Sheet row notation to index');
}
return num - 1;
}
Thank you to #Mogsdad as this is a small modification to your answer, although I believe my solution is less efficient.

What is the fastest way to count the number of significant digits of a number?

What is the fastest way to count the number of significant digits of a number?
I have the following function, which works, but is quite slow due to string operations.
/**
* Count the number of significant digits of a number.
*
* For example:
* 2.34 returns 3
* 0.0034 returns 2
* 120.5e+3 returns 4
*
* #param {Number} value
* #return {Number} The number of significant digits
*/
function digits (value) {
return value
.toExponential()
.replace(/e[\+\-0-9]*$/, '') // remove exponential notation
.replace( /^0\.?0*|\./, '') // remove decimal point and leading zeros
.length
};
Is there a faster way?
Update: here a list of assertions to test correct functioning:
assert.equal(digits(0), 0);
assert.equal(digits(2), 1);
assert.equal(digits(1234), 4);
assert.equal(digits(2.34), 3);
assert.equal(digits(3000), 1);
assert.equal(digits(0.0034), 2);
assert.equal(digits(120.5e50), 4);
assert.equal(digits(1120.5e+50), 5);
assert.equal(digits(120.52e-50), 5);
assert.equal(digits(Math.PI), 16);
My own method failed for digits(0), I fixed that by adding a ? to the second regexp.
Here's a more mathematical way of doing the same operation (which appears to be significantly faster)
JSPerf comparing the three implementations
Accurate for integer n < +-(2^53) per http://ecma262-5.com/ELS5_HTML.htm#Section_8.5
Floats are converted to a string and then coerced to an int (by removing the decimal so similar rules apply)
var log10 = Math.log(10);
function getSignificantDigitCount(n) {
n = Math.abs(String(n).replace(".", "")); //remove decimal and make positive
if (n == 0) return 0;
while (n != 0 && n % 10 == 0) n /= 10; //kill the 0s at the end of n
return Math.floor(Math.log(n) / log10) + 1; //get number of digits
}
Slight improvement of regular expression
function digits (value) {
return value
.toExponential()
.replace(/^([0-9]+)\.?([0-9]+)?e[\+\-0-9]*$/g, "$1$2")
.length
};
And yet another approach, that uses string operations and handles some special cases for better performance:
function digits(value) {
if (value === 0) {
return 0;
}
//create absolute value and
var t1 = ("" + Math.abs(value));
//remove decimal point
var t2 = t1.replace(".","");
//if number is represented by scientific notation,
//the places before "e" (minus "-" and ".") are the
//significant digits. So here we can just return the index
//"-234.3e+50" -> "2343e+50" -> indexOf("e") === 4
var i = t2.indexOf("e");
if (i > -1) {
return i;
}
//if the original number had a decimal point,
//trailing zeros are already removed, since irrelevant
//0.001230000.toString() -> "0.00123" -> "000123"
if (t2.length < t1.length) {
// -> remove only leading zeros
return t2.replace(/^0+/,'').length;
}
//if number did not contain decimal point,
//leading zeros are already removed
//000123000.toString() -> "123000"
// -> remove only trailing zeros
return t2.replace(/0+$/,'').length;
}
You can directly examine the bytes of a floating-point value by using typed arrays. The advantages of doing this are that it's fast, and it doesn't require any math to be done. You can look directly at the bits of the mantissa.
You can start with this:
var n = yourFloatingPointValue;
var f64 = new Float64Array(1);
var dv = new DataView(f64.buffer);
dv.setFloat64(0, n, false); // false -> big-endian
var bytes = [];
for (var i = 0; i < 8; i++)
bytes.push(dv.getUint8(i));
Now the bytes array contains integers representing the 8-bit values of the floating point value as it looks in memory. The first byte contains the sign bit in the top bit position, and the first 7 bits of the exponent in the rest. The second byte contains the 5 least-significant bits of the exponent and the first three bits of the mantissa. The rest of the bytes are all mantissa.
Regular string checking. A slight of improvement though.
function digits(value) {
value = "" + value;
var res = 0;
for (var i = 0, len = value.length; i < len; i++){
if (value[i]==="e")break;
if (+value[i]>=0)
res++;
}
return res;
};
jsperf Benchmark testing result as compared to the OP's and other answers code.
Update
function digits(value) {
console.log(value);
value = "" + (+value);
var res = 0;
for (var i = 0, len = value.length; i < len; i++) {
if (value[i] === "e")
{
break;
}
if (+value[i] >= 0)
{
res++;
}
}
console.log(value);
return res;
}
function check(val1, val2) {
console.log( val1+"==="+val2 +" = "+ (val1 === val2));
return val1 === val2;
}
check(digits(0), 1);
check(digits(2), 1);
check(digits(1234), 4);
check(digits("0012003400"), 8);
check(digits("0022.002200"), 6);
check(digits(2.34), 3);
check(digits(3000), 4);
check(digits(0.0034), 2);
check(digits(12003), 5);
check(digits(1.23e+50), 3);
check(digits("1.23e+50"), 3);
check(digits(120.5e51), 4);
check(digits(1120.5e+52), 5);
check(digits(120.52e-53), 5);
check(digits(Math.PI), 16);
There is a faster and indirect way to do it, which is converting it to a string and finding the length of it.
a = 2.303
sig_fig = len(str(a))-len(str(int(a)))-1
The extra -1 is for the "."

How to create a Alphanumeric Serial Number in Javascript?

I am trying to create a alphanumeric serial number in Javascript, the serial number is governed by the following rules:
3-Digit Alphanumeric Series
Allowed values 1-9 (Zero is excluded) and A-Z (All Capitals with exclusions of I and O)
The code should be able to give the next number after getting the input number.
The last part is tricky, basically the code would fetch the existing value of the serial number and it would then give the output as the next number.
For example: If the input number 11D then the output number should be 11E. Please let me know if this description is good enough to explain my requirement.
The excel sheet for the same is attached here
Also the part of the code where the script would fetch the starting value 11D would be from this code:
cur_frm.add_fetch('item_group','serial_number','serial_number');
This should do it:
var nextSerialNumber = function(serialNumber) {
return (parseInt(serialNumber, 36) + 1).toString(36).replace(
/i/g,'j').replace(/o/g, 'p').replace(/0/g, '1').toUpperCase();
}
nextSerialNumber("99Z") //=> "9A1"
nextSerialNumber("11D") //=> "11E"
I'm not sure what you want to happen after ZZZ. It jumps to 1111, but that could be changed.
If you input an invalid serial number (e.g. 11I), it gives you the next valid number (e.g. 11J).
var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
var alphabetLen = alphabet.length;
function nextDigit(digit) {
nextDigitPos = (alphabet.indexOf(digit)+1) % alphabetLen;
return alphabet.charAt(nextDigitPos);
}
/**
* Computes the next serial id.
* #param id the id to compute the successor of,
* if null or empty String the first id
* "111" is returned.
*/
function nextSerial(id) {
if(id==null || id.length==0) return "111";
var digits = id.split("");
digits[2] = nextDigit(digits[2]);
if(digits[2] == "1") /* overflow */ {
digits[1] = nextDigit(digits[1]);
if(digits[1] == "1") /* overflow */ {
digits[0] = nextDigit(digits[0])
}
}
return digits.join("");
}
This should do it:
function getNext(num) {
var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
var digits = num.toUpperCase().split(""),
len = digits.length,
increase = true;
if (len != 3)
throw new Error("Invalid serial number length in getNext: "+num);
for (var i=len-1; increase && i>=0; i--) {
var val = alphabet.indexOf(digits[i]);
if (val == -1)
throw new Error("Invalid serial number digit in getNext: "+num);
val++;
if (val < alphabet.length) {
digits[i] = alphabet[val];
increase = false;
} else { // overflow
digits[i] = alphabet[0];
}
}
if (increase) // is still true
throw new Error("Serial number overflow in getNext");
num = digits.join("");
return num;
}
Since you are working with a nearly alphanumeric alphabet, a parseInt/toString with radix 33 might have done it as well. Only you need to "jump" over the 0, I and O, that means replacing 0,A,B… by A,B,C…, replacing H,I,J… by J,K,L… and replacing M,N,O… by P,Q,R… (and everything back on deserialisation) - which might be OK if JS has a numeric char datatype, but I think it's easier to do it manually as above.
If you're curious:
String.prototype.padLeft = function(n, x) {
return (new Array(n).join(x || "0")+this).slice(-n);
};
function getNext(num) {
var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
var back = {}, forth = {};
for (var i=0; i<alphabet.length; i++) {
var a = alphabet[i],
b = i.toString(36);
back[a] = b;
forth[b] = a;
}
return (parseInt(num.replace(/./g, function(c) {
return back[c]; // base33 from alphabet
}), alphabet.length) + 1)
.toString(alphabet.length)
.padLeft(3)
.replace(/./g, function(c) {
return forth[c]; // base33 to alphabet
});
}

Categories