0

I am writing a code to check if a certain number of credit card number inputs is valid or not. I have written:

function validCreditCard(value) {
    // accept only digits, dashes or spaces
    if (value.trim().length <= 1) return 'Value entered must be greater than 1';
    if (/[^0-9\s]+/.test(value)) return false;

    // Remove all white spaces
    value = value.replace(/\D/g, '');

    for (var i = 0; i < value.length; i++) {
    // code goes here
    // Loop through the string from the rightmost moving left and double the value of every second digit.
}

I have been trying to wrap my head around how to Loop through the string from the rightmost moving left and double the value of every second digit since the length of the string can be even or odd. For example, for an input with length 16(even), the first number to the left will be the 15th position (index 14) and for an input with odd length like 11, the first number to the left will be the 10th position(index 9). I have written a program that didn't work for both cases and now I want to write one to cater for both. How best can I achieve this without creating two separate checks to see if the length of the input is odd or even?

P.S: Some implementations online don't work for both cases too.

camelCase
  • 475
  • 5
  • 14
  • 1
    "Double the value of every second digit"? What happens if the digit is greater than 4? Can you post an example or two with your current code? – CertainPerformance Feb 03 '19 at 07:00
  • A simple solution: prefix the input with an extra zero if its length is odd. Now you only have one case. – trincot Feb 03 '19 at 08:11

2 Answers2

1

To know which are the "every second digit from the right" you actually don't really need to iterate from the end to the start. It can also be done from left to right. To know whether a digit is such a "second" digit, compare its odd/even parity with the parity of the length of the input.

So like this:

var parity = value.length % 2;
for (var i = 0; i < value.length; i++) {
    if (i % 2 === parity) {
        // "Special" treatment comes here
    } else {
        // "Normal" treatment comes here
    }
}

But you could also just prefix a zero to the input if it has an odd length, to give it an even length:

if (value.length % 2) value = '0' + value;
for (var i = 0; i < value.length; i++) {
    if (i % 2 === 0) {
        // "Special" treatment comes here
    } else {
        // "Normal" treatment comes here
    }
}

What you are implementing is called the Luhn algorithm. So with the use of the length's parity, it could look like this:

function validCreditCard(value) {
    // Accept only digits, dashes or spaces
    if (/[^0-9\s-]+/.test(value)) return false;
    // Get the digits only
    value = value.replace(/\D/g, "");
    // Need at least one digit
    if (!value.length) return false;
    var parity = value.length % 2;
    var sum = 0;
    for (var i = 0; i < value.length; i++) {
        sum += i % 2 === parity
            ? (value[i]*2) % 10 + (value[i] > '4') // Double, and add one if double > 9
            : +value[i]; // Normal case
    }
    return sum%10 === 0;
}

console.log(validCreditCard("4024007112651582"));

Explanation:

In the loop I have replaced the if...else... construct with the conditional ternary operator -- ... ? ... : ... -- which is kind of practical when in both cases you need to assign a value to a variable (to sum in our case).

For "normal" digits the sum must be increased with the digit's value (+value[i]). The plus unary operator will convert the character to its numerical value -- so +'1' becomes 1.

For the "special" digits, sum must be increased with twice the digit's value (value[i]*2). Note that here the conversion from string to integer is happening automatically because of the multiplication.

Then we need to deal with the case where this double value consists of two digits. For example: 8*2 = 16. In that case the resulting digit should not be 6, but 7. So we add (value[i] > '4'). That really is boolean expression (false or true): it is true when the double has two digits. By adding the boolean value, it is coerced to 0 or 1 respectively -- exactly what we need.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • kindly explain what goes on inside the for loop. – camelCase Feb 03 '19 at 18:12
  • 1
    Explanation added to my answer. – trincot Feb 03 '19 at 19:00
  • Wow! `(value[i]*2) % 10 + (value[i] > '4')`. In the case of 5, `(5*2)%10 + (value[i] > '4') = 10 % 10 + ('5' > '4') = 0 + 1(true) = 1`, For 6, `(6*2) % 10 + (value[i] > '4') = 12 % 10 + ('6' > '4') = 2 + 1(true) = 3`, `(7*2) % 10 + (value[i] > '4') = 14 % 10 + ('7' > '4') = 4 + 1(true) = 5...` For all single digits, `(value[i]) > '4')` will always result to false(0) and be added to the remainder which will always be the number. The way you used the parity(0 and 1) to step through all in one line is simply chic.I never would have thought of this way, thanks for the knowledge shared! – camelCase Feb 04 '19 at 08:13
0

Loop through the string from the rightmost moving left and double the value of every second digit

You can use .reverse(), .map() and remainder operator %

let arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];

var res = [...arr].reverse().map((a, i) => i % 2 ? a * 2 : a).reverse();

console.log(res);
guest271314
  • 1
  • 15
  • 104
  • 177