1

I was creating a function for Luhn's algorithm in Javascript. Here is my code:

// All valid credit card numbers
const valid1 = [4, 5, 3, 9, 6, 7, 7, 9, 0, 8, 0, 1, 6, 8, 0, 8];
const valid2 = [5, 5, 3, 5, 7, 6, 6, 7, 6, 8, 7, 5, 1, 4, 3, 9];
const valid3 = [3, 7, 1, 6, 1, 2, 0, 1, 9, 9, 8, 5, 2, 3, 6];
const valid4 = [6, 0, 1, 1, 1, 4, 4, 3, 4, 0, 6, 8, 2, 9, 0, 5];
const valid5 = [4, 5, 3, 9, 4, 0, 4, 9, 6, 7, 8, 6, 9, 6, 6, 6];

// All invalid credit card numbers
const invalid1 = [4, 5, 3, 2, 7, 7, 8, 7, 7, 1, 0, 9, 1, 7, 9, 5];
const invalid2 = [5, 7, 9, 5, 5, 9, 3, 3, 9, 2, 1, 3, 4, 6, 4, 3];
const invalid3 = [3, 7, 5, 7, 9, 6, 0, 8, 4, 4, 5, 9, 9, 1, 4];
const invalid4 = [6, 0, 1, 1, 1, 2, 7, 9, 6, 1, 7, 7, 7, 9, 3, 5];
const invalid5 = [5, 3, 8, 2, 0, 1, 9, 7, 7, 2, 8, 8, 3, 8, 5, 4];

// Can be either valid or invalid
const mystery1 = [3, 4, 4, 8, 0, 1, 9, 6, 8, 3, 0, 5, 4, 1, 4];
const mystery2 = [5, 4, 6, 6, 1, 0, 0, 8, 6, 1, 6, 2, 0, 2, 3, 9];
const mystery3 = [6, 0, 1, 1, 3, 7, 7, 0, 2, 0, 9, 6, 2, 6, 5, 6, 2, 0, 3];
const mystery4 = [4, 9, 2, 9, 8, 7, 7, 1, 6, 9, 2, 1, 7, 0, 9, 3];
const mystery5 = [4, 9, 1, 3, 5, 4, 0, 4, 6, 3, 0, 7, 2, 5, 2, 3];

// An array of all the arrays above
const batch = [valid1, valid2, valid3, valid4, valid5, invalid1, invalid2, invalid3, invalid4, invalid5, mystery1, mystery2, mystery3, mystery4, mystery5];


// Add your functions below:
function validateCred(arr){
  let newArr = [];
  newArr.push(arr[arr.length - 1]);
  for(let i = arr.length - 2; i >= 0; i--){
    let x = arr[i];
    // console.log(x);
    x *= 2;
    if(x > 9){
      x -= 9;
    }
    // console.log(x);
    newArr.push(x);
    // console.log(newArr);
  } 
  // console.log(newArr);
   let sum = 0;
   newArr.forEach(num => sum += num);
   console.log(sum);
//    if(sum % 10 === 0){
//     return true;
//   } else {
//      return false;
//  }
  return newArr;
}
console.log(validateCred(valid1));

This is not my original code, but as you can see validateCred is my function. The if statement is actually supposed to give a result of true, but it is giving false, so I was wondering where I went wrong. The console.log's in here were helping me figure out what went wrong, so here is the output of them. I cut the code to see what the function was doing with the array, I checked and everything was good. Even the sum was working. So I was left wondering whether the array is wrong, but when I inputed the same array in GeeksForGeeks's implementation, It gave true! So what went wrong?

Jandroid
  • 111
  • 1
  • 11
  • "Take the original number and starting from the rightmost digit moving left, double the value of every **second** digit (including the rightmost digit)." (emphasis mine) – Niet the Dark Absol Oct 31 '21 at 07:04
  • But are you saying that we need to start this sequence after the check digit? – Jandroid Oct 31 '21 at 07:07
  • Here's what my list told: Starting from the farthest digit to the right, AKA the check digit, iterate to the left. As you iterate to the left, every other digit is doubled (the check digit is not doubled). If the number is greater than 9 after doubling, subtract 9 from its value. Sum up all the digits in the credit card number. If the sum modulo 10 is 0 (if the sum divided by 10 has a remainder of 0) then the number is valid, otherwise, it’s invalid. – Jandroid Oct 31 '21 at 07:08
  • That's right. `valid1` should give you a sum of `80`, which is indeed a multiple of 10. – Niet the Dark Absol Oct 31 '21 at 07:17
  • wait, so you are saying that i should be arr.length - 2 – Jandroid Oct 31 '21 at 07:17
  • It may be easier to create a reversed copy of the number (`const copy = [...arr].reverse()`) and iterate over that, because then you can use `i%2` to decide whether to double or not. You could also pre-calculate the "double but subtract 9 if too big" thing and have an array like `const doubled = [0,2,4,6,8,1,3,5,7,9]`, then reference `doubled[x]` to skip all of the maths. – Niet the Dark Absol Oct 31 '21 at 07:20
  • but here's the thing: i % 2 might or might not work depending on arr.length – Jandroid Oct 31 '21 at 07:22
  • That's why you reverse the array first. – Niet the Dark Absol Oct 31 '21 at 07:22
  • so maybe I should test arr.length and then do I % 2 – Jandroid Oct 31 '21 at 07:22
  • With that in place, you can just loop over the array once, and do `sum += (i%2) ? doubled[x] : x` to add up the digits in the correct way. – Niet the Dark Absol Oct 31 '21 at 07:23
  • okay then, I'll try it – Jandroid Oct 31 '21 at 07:24
  • Hi niet, i used two for loops in my implementation, but I read your's as well, thanks! – Jandroid Oct 31 '21 at 08:22
  • It would be helpful if you could move all your comments into an answer. – Jandroid Oct 31 '21 at 08:24

1 Answers1

1

I don't want to take Niet's credits, so if he posts his comments as an anwser, you should accept his. I'm posting this only two show two shorter alternatives using Array.prototype.reduce

The main problem with your algorithm is (as Niet already said) you are doubling each digit, instead of only every other digit.

Variant 1 with reversing the card number

function checkCard(arr) {
  return arr.slice().reverse() 
    .reduce((a, c, i) => a + (i % 2 == 0
      ? c
      : (c < 5 ? 2 * c : 2 * c - 9)), 
      0) % 10 == 0;
}

I used arr.slice().reverse() because reverse works in place. So to keep arr in the original order, copy it first. reduce will then sum up the digits. For every even index (0,2,4,...) in the reversed array it will just add the current digit c. For every odd (1,3,5,...) index, it will add either 2*c or 2*c-9, depending on whether 2*c > 9.

Variant 2 without reversing the card number

function checkCard2(arr) {
  const p = arr.length % 2
  return arr.reduce((a,c,i) => a + (i % 2 != p 
    ? c
    :  (c < 5 ? 2 * c : 2 * c - 9)),
    0) % 10 == 0;
}

Because Luhn's original algorithm starts couting from the rightmost digit, if we start from the leftmost digit (ie without reversing the number), we have to act differently on numbers of even or odd length. For numbers of odd length, we must double the digits at odd indexes (1,3,5,...), for even length we must double the digits at even indexes (0,2,4,...). That's what the p is for. It will be 0 for even length, and 1 for odd length. So if i % 2 == p we must double the current digit, otherwise not.

derpirscher
  • 14,418
  • 3
  • 18
  • 35