I'm currently working on a project involving validating credit cards, and I have this luhn algorithm:
export default function luhn(card: string): boolean {
return (
card.split('').reduceRight(function(prev: any, curr: any, idx) {
prev = parseInt(prev, 10)
if ((idx + 1) % 2 !== 0) {
curr = (curr * 2)
.toString()
.split('')
.reduce(function(p, c) {
return (parseInt(p, 10) + parseInt(c, 10)) as any
})
}
return prev + parseInt(curr, 10)
}, 0) %
10 ===
0
)
}
Notice the boolean in there? i'm using this as a module for my index.ts which looks like this:
import { createReadStream } from 'fs'
import { createInterface } from 'readline'
import { getCompanyName } from './cardType'
import cardValidator from './cardValidator'
const lineReader = createInterface({
input: createReadStream('data/input.txt')
})
lineReader.on('line', (creditCard: string) => {
var company = `${getCompanyName(creditCard)}: ${creditCard} (${cardValidator(
creditCard
)})`
console.log(company)
})
It currently outputs to the console something like this:
MasterCard: 5105105105105100 (true)
MasterCard: 5105105105105106 (false)
I want the true/false to be valid/invalid I've looked into it a little bit(w3) but don't know how to integrate anything into my algorithm as it requires you to declare a variable for both true and false. any suggestions/answers would be much appreciated
You can simply use a ternary operator to include the strings you want directly in your output string:
var company = `${getCompanyName(creditCard)}: ${creditCard} (${cardValidator(creditCard) ? "valid" : "invalid"})`
Related
I am trying to override mathjs Bignumber using:
import * as math from 'mathjs';
export const bgn = (v: number | math.BigNumber) => {
const z = math.bignumber(v) as math.BigNumber;
(z as any).toJSON = () => {
return Number(math.larger(100, z) ? math.round(z,2) : math.round(z,4)).toFixed(4);
}
return z;
}
but for some reason, it's still stringifying it to:
{"mathjs":"BigNumber","value":"42500"}
my goal is to stringify it to a number:
42500
This is not currently possible with the native JSON.stringify implementation. It will become possible with the adoption of the JSON.parse source text access proposal which also includes a helper for non-lossy serialization.
You'd use it as
const text = JSON.stringify(value, (key, val) => {
if (val instanceof math.bignumber) return JSON.rawJSON(val.toString())
else return val;
});
console.log(text);
OP was on the right track, this should work fine:
const math = require('mathjs');
const bgn = (v) => {
const z = math.bignumber(v); // as math.BigNumber;
(z).toJSON = () => {
return Number(math.larger(z, 100) ? math.round(z, 3) : math.round(z, 5));
}
return z;
}
console.log(JSON.stringify(bgn(5.555555444)));
in JS instead of TS.
So I know max call stack error is appearing when infinite loop is in occurring, by recursion. But I'm having this weird problem. Everything in code under works fine, until I add these 2 commented lines under randomRow and randomCol. Is it possible to create infinite loop with random numbers or am I missing something?
function insertNumbers(freeRCPos, grid, fields, num) {
//base case
if (fields.length === 0) return grid;
let randomRow = Math.floor(Math.random() * maxRows);
let randomCol = Math.floor(Math.random() * maxRows);
// if (grid[randomRow][randomCol].value !== undefined)
// return insertNumbers(freeRCPos, grid, fields, num);
let newFields = [];
//there are in total 9 fields so it has to go through all of them
for (let field of fields) {
//check if its already in field
if (field.includes(JSON.stringify([randomRow, randomCol]))) {
let res = freeRCPos.map((element: any) =>
element.map((pos: any) =>
pos === JSON.stringify([randomRow, randomCol]) ? true : false
)
);
if (
res.filter((el: any) => (el.includes(true) ? true : false)).length > 0
) {
grid[randomRow][randomCol].value = num;
newFields= fields.filter(
(_: any, fieldId: number) => fields.indexOf(field) !== fieldId
);
//return with modified fields and others to be able to complete base case
return insertNumbers(
freeRCPos.map((field: any) =>
field.map((pos: string) =>
pos?.startsWith("[" + randomRow) || pos?.endsWith(randomCol + "]")
? null
: pos
)
),
grid,
newFields,
num,
);
//else return same
} else {
return insertNumbers(freeRCPos, grid, fields, num);
}
//else return same
} else {
return insertNumbers(freeRCPos, grid, fields, num);
}
}
return grid;
}
I've maybe put a lot of unnecessary code here so if I need to shorten it, just ask. Appreciate the help.
If the random numbers are the existing indexes in grid (= if grid[randomRow][randomCol].value !== undefined is true), it will never pass the commented part and it will keep calling itself with the same values. You need to update something before calling the function recursively and get closer to the base case which is fields.length === 0.
There's the standard npm semver version comparison library, but I have some simple logic to compare semver versions here:
const versionA = '14.8.3';
const versionB = '15.1.1';
const versionC = '15.1.2';
const semver = require('semver');
const assert = require('assert');
const isGreater = (a, b) => {
const [majorA, minorA, patchA] = String(a).split('.').map(v => Number.parseInt(v));
const [majorB, minorB, patchB] = String(b).split('.').map(v => Number.parseInt(v));
if (majorA > majorB) {
return true;
}
if (majorB > minorA) {
return false;
}
if (minorA > minorB) {
return true;
}
if (minorB > minorA) {
return false;
}
if (patchA > patchB) {
return true;
}
if (patchB > patchA) {
return false;
}
return false;
};
assert(isGreater(versionB, versionA), 'version b should be greater.');
assert(isGreater(versionA, versionB), 'version b should be greater.');
my question is - is there a way to simplify the logic in the greaterThan function? This function is supposed to replicate the logic in semver.gt().
You can use localeCompare instead, with the numeric option (with numeric, comparison is such that "1" < "2" < "10"), which is exactly the logic you're looking for:
const versionA = '14.8.3';
const versionB = '15.1.1';
const versionC = '15.1.2';
const versionD = '15.1.10';
const versionE = '15.2.1';
const versionF = '15.11.1';
const isGreater = (a, b) => {
return a.localeCompare(b, undefined, { numeric: true }) === 1;
};
// first argument version comes later than second argument:
console.log(isGreater(versionB, versionA));
console.log(isGreater(versionC, versionB));
console.log(isGreater(versionD, versionC));
console.log(isGreater(versionE, versionD));
console.log(isGreater(versionF, versionE));
console.log('---');
// second comes before first:
console.log(isGreater(versionA, versionB));
// same, return value should be false:
console.log(isGreater(versionA, versionA));
Or, equivalently, you can pass the locale string
en-US-u-kn-true
as the second parameter instead of { numeric: true }.
I believe this is logically the same and shorter, but not exactly stunning in it's simplicity
const parseInt = (v: string) : number => {
const num = Number.parseInt(v);
if(!(Number.isInteger(num) && num > 0)){
throw new Error('Could not parse positive integer from string')
}
return num;
};
const isGreater = (a: string, b: string) : boolean => {
const [majorA, minorA, patchA] = String(a).split('.').map(parseInt);
const [majorB, minorB, patchB] = String(b).split('.').map(parseInt);
if (majorA !== majorB) {
return majorA > majorB;
}
if (minorA !== minorB) {
return minorA > minorB;
}
return patchA > patchB;
};
I was given this problem by my friend. The question asks to remove all the alphabetically consecutive characters from the string input given.
So I did it using Javascript, I need expert help if I performed it precisely.
I thought using Array.prototype.reduce will be the best way, do we have other possible ways?
/**
* #author Abhishek Mittal <abhishekmittaloffice#gmail.com>
* #description function can deal with both any types followed in a consecutive manner in ASCII Chart.
* #param {string} str
*/
function improvise(str) {
// Backup for original input.
const bck = str || '';
const bckArr = bck.split('').map( e => e.charCodeAt(0)); // converting the alphabets into its ASCII for simplicity and reducing the mayhem.
let result = bckArr.reduce( (acc, n) => {
// Setting up flag
let flag1 = n - acc.rand[acc.rand.length - 1];
let flag2 = n - acc.temp;
if(flag1 === 1 || flag2 === 1) {
(flag2 !== NaN && flag2 !== 1) ? acc.rand.pop() : null; // update the latest value with according to the case.
acc.temp = n
}else{
acc.rand.push(n); // updating the random to latest state.
acc.temp = null;
}
return acc;
}, {rand: [], temp: null} /* setting up accumulative situation of the desired result */)
result = result.rand.map(e => String.fromCharCode(e)).join('')
return result ? result : '' ;
}
function init() {
const str = "ab145c";
const final = improvise(str);
console.log(final)
}
init();
Well, the output is coming out to be correct.
Input: ab145c
Output: 1c
There's no way to solve this using any remotely reasonable regular expression, unfortunately.
I think it would be a lot clearer to use .filter, and check whether either the next character or the previous character is consecutive:
const code = char => char
? char.charCodeAt(0)
: -2; // will not be === to any other codes after addition or subtraction
function improvise(str) {
return [...str]
.filter((char, i) => {
const thisCode = code(char);
return (
thisCode !== code(str[i - 1]) + 1
&& thisCode !== code(str[i + 1]) - 1
);
})
.join('');
}
console.log(improvise('ab145c'));
(alternatively, you could check only whether the next character is consecutive, but then you'd have to check the validity of the last character in the string as well)
If you need continuously replace characters until no consecutive characters remain, then keep calling improvise:
const code = char => char
? char.charCodeAt(0)
: -2; // will not be === to any other codes after addition or subtraction
function improvise(str) {
return [...str]
.filter((char, i) => {
const thisCode = code(char);
return (
thisCode !== code(str[i - 1]) + 1
&& thisCode !== code(str[i + 1]) - 1
);
})
.join('');
}
let result = 'hishakmitalaaaaabbbbbbcccccclmnojqyz';
let same = false;
while (!same) {
const newResult = improvise(result);
if (newResult !== result) {
result = newResult;
console.log(result);
} else {
same = true;
}
}
console.log('FINAL:', result);
Well, that's great code but it doesn't gives the exact solution to the problem, see:
INPUT: hishakmitalaaaaabbbbbbcccccclmnojqyz
i got
Output: shakmitalaaaabbbbcccccjq
you see 'ab' & 'bc' remains intact, we need to increase the number of loops maybe to check those, can increase complexity as well.
But, my solution doesn't as well gives me the answer I desire e.g. for the above string I should get
ishakmitalaacccjq
but rather my solution gives
shakmitalcccccjq
hope you understand the question. We need a change in the way we traverse throughout the string.
I checked the documentation. What I would like is for my numbers to have four digits and leading zeros.
22 to 0022
1 to 0001
Can someone help and tell me if this is possible with the number or another kind of filter?
No filter required, Just use an expression in your html
{{("00000"+1).slice(-6)}} // '000001'
{{("00000"+123456).slice(-6)}} // '123456'
Let's say you have a module called myModule in your app myApp:
angular.module('myApp', ['myModule']);
Define your filter in in this module:
angular.module('myModule', [])
.filter('numberFixedLen', function () {
return function (n, len) {
var num = parseInt(n, 10);
len = parseInt(len, 10);
if (isNaN(num) || isNaN(len)) {
return n;
}
num = ''+num;
while (num.length < len) {
num = '0'+num;
}
return num;
};
});
Use your filter in markup:
{{myValue | numberFixedLen:4}}
Keeping it minimal... (works with both strings & numbers) Do some validation if you have to (isNumber, NaN)
// 1e8 is enough for working with 8 digits (100000000)
// in your case 1e4 (aka 10000) should do it
app.filter('numberFixedLen', function () {
return function(a,b){
return(1e4+""+a).slice(-b);
};
});
If you want it even smaller and the browser supports arrow function or you are using babel/traceur then it could be reduced to:
app.filter('numberFixedLen', () => (a, b) => (1e4 + "" + a).slice(-b))
html:
{{ myValue | numberFixedLen:4 }}
Note This has less flexibility and this will only work for numbers lower then 10000 if it's a bigger number you would have to increase both 4 and 1e4 or use any other dynamic solution.This was intended to do as little as possible as fast as possible.
It is intentionally the same thing as doing:
("10000"+1234567).slice(-4) // "4567"
("10000"+1234567).slice(-9) // "001234567"
Update You could also use padStart (but it doesn't work in IE)
// app.filter('numberFixedLen', () => (a, b) => ("" + a).padStart(b, 0))
console.log("1234567".padStart(4, 0)) // "1234567"
console.log("1234567".padStart(9, 0)) // "001234567"
Minimum code with underscore.string's padding function and the angular-underscore-string filter:
working demo in jsFiddle
angular string input -> angular-underscore-string filter -> underscore.string
<div ng-app="app" ng-controller="PadController">
<div ng-repeat="num in nums">{{ num | s: 'pad':[4, '0'] }}</div>
</div>
angular.module('app', ['underscore.string']).controller('PadController', function ($scope) {
$scope.nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
});
// 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
The s variable refers to the string library. In older versions you might have to substitute it for "_.str", ie. {{ num | _.str: 'pad':[4, '0'] }}
If you are dealing exclusively with "0" padding and don't mind tailoring your filters by use case, I'd go with something similar to Endless's answer for speed but would recommend you make sure the number isn't already long enough with something like:
app.filter('minLength', function () {
return function(input,len){
input = input.toString();
if(input.length >= len) return input;
else return("000000"+input).slice(-len);
}
});
As this will not only save it from trimming numbers or strings that already satisfy the minimum length which is important to avoid weird stuff like:
{{ 0.23415336 | minLength:4 }} //Returns "0.23415336" instead of "5336" like in Endless's code
But by using "000000" instead of a number like 1e6 you avoid both changing the actual value of the input (by not adding 1000000 to it) and avoid the need to implicitly convert the number to a string thereby saving a computational step considering the input would already be a converted to a string to avoid the clipping issue mentioned above.
If you want a system that doesn't need any use-case testing that's both faster and more flexible than bguiz's solution I use a filter like:
app.filter('minLength', function(){
return function(input, len, pad){
input = input.toString();
if(input.length >= len) return input;
else{
pad = (pad || 0).toString();
return new Array(1 + len - input.length).join(pad) + input;
}
};
});
This allows you to do the standard:
{{ 22 | minLength:4 }} //Returns "0022"
But also gives you the option to add non-zero padding options like:
{{ 22 | minLength:4:"-" }} //Returns "--22"
and you can enforce wacky stuff with numbers or strings like:
{{ "aa" | minLength:4:" " }} //Returns " aa"
Plus, if the input is already longer than your desired length, the filter will just pop it back out without any trimming:
{{ 1234567 | minLength:4 }} //Returns "1234567"
You also avoid the need to add validation for len because when you call the filter without a len argument, angular will throw a RangeError in your console at the line where you try to create an array of length null making it simple to debug.
You could just use pure JavaScript such as
('00000'+refCounter).substr(-5,5)
for padding with 5 zeros the value of refCounter.
NOTE: Make sure to check that refCounter is not undefined, otherwise you'll get an exception.
Pls use the below filter modify if required for some modifications
app.filter('customNo', function () {
return function (input) {
var n = input;
return (n < 10) ? '000' + n : (n < 100) ? '00' + n : (n < 1000) ? '0' + n : '' + n;
}
});
<span>{{number|customNo}}</span>
Another example:
// Formats a number to be at least minNumberOfDigits by adding leading zeros
app.filter('LeadingZerosFilter', function() {
return function(input, minNumberOfDigits) {
minNumberOfDigits = minNumberOfDigits || 2;
input = input + '';
var zeros = new Array(minNumberOfDigits - input.length + 1).join('0');
return zeros + input;
};
});
The cleanest would be to create your own filter in angular and use it. There's already a few answers for this but personally I find this easiest to read. Create a array by the length of zeros needed then join the array with zero.
myApp.filter('customNumber', function(){
return function(input, size) {
var zero = (size ? size : 4) - input.toString().length + 1;
return Array(+(zero > 0 && zero)).join("0") + input;
}
});
Use it like:
{{myValue | customNumber}}
I also made it so you can specify leading zeros as a parameter:
{{myValue | customNumber:5}}
Demo:
http://www.bootply.com/d7SbUz57o8
This is an Angular 1.x variant of the directive in TypeScript which would handle both integers and decimals. Everything is considered 'type safe' thanks to TS.
adminApp.filter("zeroPadNumber",
() => (num: number, len: number): string => {
if (isNaN(num) || isNaN(len)) {
return `${num}`;
}
if (`${num}`.indexOf(".") > -1) {
var parts = `${num}`.split(".");
var outDec = parts[0]; // read the before decimal
while (outDec.length < len) {
outDec = `0${outDec}`;
}
return outDec + parts[1]; // reappend the after decimal
} else {
var outInt = `${num}`;
while (outInt.length < len) {
outInt = `0${outInt}`;
}
return outInt;
}
});
I've extended #bguiz's answer to be able to handle arrays, which was my requirement for using the filter on ng-options:
app.filter('numberFixedLen', function () {
return function (p, len) {
function toFixedLength(n, len) {
var num = parseInt(n, 10);
len = parseInt(len, 10);
if (isNaN(num) || isNaN(len)) {
return n;
}
num = '' + num;
while (num.length < len) {
num = '0' + num;
}
return num;
}
if (p.length) {
for (var i = 0; i < p.length; i++) {
p[i] = toFixedLength(p[i], len);
}
} else {
p = toFixedLength(p, len);
}
return p;
};
});
Example code:
{{ (value > 9 ? (value > 99 ? (value > 999 ? value : '0'+value) : '00'+value) : '000'+value) }}