Luhn Algorithm Logic - javascript

I am currently going through Codecademy's Full Stack Engineer course, up until now I have been perfectly fine with it, discovering new things, working out problems on my own, but this is a serious roadblock in my progression as I just can't seem to identify the problem with this logic. I don't mean to question Luhn's algorithm but seriously I need some clarification on this...
So my problem is, that the algorithm is returning all my arrays as valid, my code is below (arrays provided by codecademy):
// 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];
And my function implementing the algorithm:
const validateCred = arr => {
let checkSum = 0;
let ifEvenDouble = 0;
arr.push(checkSum);
//Iterate through array, double what is needed
for(let i = arr.length - 2; i >= 0; i--){
console.log(ifEvenDouble);
//If ifEvenDouble is even, we are at the 'other' cell
if((ifEvenDouble % 2) === 0){
let doubled = arr[i] * 2;
//If doubled digit is greater than 9, store sum of individual digits
//Convert the doubled number to a string then extract each member and convert back to number for calculation, add to checkSum and skip to next iteration, otherwise, add arr[i]
let newDigit = 0;
if(doubled > 9){
newDigit = Number(doubled.toString()[0]) + Number(doubled.toString()[1]);
//Add doubled & split digit to total and continue the loop
checkSum += newDigit;
ifEvenDouble++;
continue;
}
//Add doubled digit less than 9 to total and continue the loop
checkSum += doubled;
ifEvenDouble++;
continue;
}
//Add current array member to total
checkSum += arr[i];
ifEvenDouble++;
}//End for loop
console.log(checkSum);
const checkDigit = (checkSum * 9) % 10;
const totalSum = checkDigit + checkSum;
if(totalSum % 10 === 0){
console.log('Valid');
return true;
} else {
console.log('Invalid');
return false;
}
};
validateCred(invalid1); // -> Output: Valid
From my understanding, my totalSum is always going to be a multiple of 10, if I'm subtracting my unit digit from 10, adding it to my checkSum is always going to give me a multiple of 10. Am I wrong?
Edit: I have been attempting to debug this already but the more I do the further away from the core algorithm I stray.
Edit(2): So thanks to the guys below, I think my issue was generating my own check digit as opposed to using one already provided? My confusion is that from reading the wikipedia page on this, it says:
'Example for computing check digit:
Assume an example of an account number "7992739871" that will have a check digit added, making it of the form 7992739871x'
And then they proceeded to do all their calculations with the numbers besides x, i think this is the main confusion now.

Your algorithm is unnecessarily complicated. Wikipedia describes it succinctly, just implement the 3 steps
From the rightmost digit (excluding the check digit) and moving left, double the value of every second digit. The check digit is neither doubled nor included in this calculation; the first digit doubled is the digit located immediately left of the check digit. If the result of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then add the digits of the result (e.g., 16: 1 + 6 = 7, 18: 1 + 8 = 9) or, equivalently, subtract 9 from the result (e.g., 16: 16 − 9 = 7, 18: 18 − 9 = 9).
Take the sum of all the digits (including the check digit).
If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; otherwise it is not valid.
I also think you misunderstood what the check digit is. You appeared to be appending it to the array as 0, and trying to calculate it at the end. It's already there in the number - it's the final digit.
const validateCred = arr => {
let doubleIt = true;
let sum = 0;
// From the rightmost digit excluding check digit...
for(let i = arr.length - 2; i >= 0; i--){
if(doubleIt){
let doubled = arr[i] * 2;
if(doubled > 9){
doubled -= 9
}
sum += doubled
}
else {
sum += arr[i]
}
doubleIt = !doubleIt;
}
// Add the check digit to the sum
sum += arr[arr.length-1];
// If sum is divisible by 10 it is valid
if(sum % 10 === 0){
console.log('Valid');
return true;
} else {
console.log('Invalid');
return false;
}
};
const invalid1 = [4, 5, 3, 2, 7, 7, 8, 7, 7, 1, 0, 9, 1, 7, 9, 5];
const valid1 = [4, 5, 3, 9, 6, 7, 7, 9, 0, 8, 0, 1, 6, 8, 0, 8];
validateCred(invalid1);
validateCred(valid1);
The places you went wrong were mainly around the use of the check digit. You appeared to be calculating it whereas its already there as just the final element in the array. The below snippet is a lot closer to your original, just without the calculation of check digit.
const validateCred = arr => {
let ifEvenDouble = 0;
let checkSum=0
//Iterate through array, double what is needed
for(let i = arr.length - 2; i >= 0; i--){
//If ifEvenDouble is even, we are at the 'other' cell
if((ifEvenDouble % 2) === 0){
let doubled = arr[i] * 2;
//If doubled digit is greater than 9, store sum of individual digits
//Convert the doubled number to a string then extract each member and convert back to number for calculation, add to checkSum and skip to next iteration, otherwise, add arr[i]
let newDigit = 0;
if(doubled > 9){
newDigit = Number(doubled.toString()[0]) + Number(doubled.toString()[1]);
//Add doubled & split digit to total and continue the loop
checkSum += newDigit;
ifEvenDouble++;
continue;
}
//Add doubled digit less than 9 to total and continue the loop
checkSum += doubled;
ifEvenDouble++;
continue;
}
//Add current array member to total
checkSum += arr[i];
ifEvenDouble++;
}//End for loop
const checkDigit = arr[arr.length-1]
const totalSum = checkDigit + checkSum;
if(totalSum % 10 === 0){
console.log('Valid');
return true;
} else {
console.log('Invalid');
return false;
}
};
const invalid1 = [4, 5, 3, 2, 7, 7, 8, 7, 7, 1, 0, 9, 1, 7, 9, 5];
const valid1 = [4, 5, 3, 9, 6, 7, 7, 9, 0, 8, 0, 1, 6, 8, 0, 8];
validateCred(invalid1);
validateCred(valid1);

I know the question was more about where you went wrong than about a better solution; the answer from Jamiec already covers that quite well.
However, with a dollop of array methods, we should be able to write a much simpler answer.
// utility functions
const sum = (ns) => ns .reduce ((a, b) => a + b, 0)
const last = (xs) => xs [xs .length - 1]
// helper function
const doubleDig = (d) => 2 * d > 9 ? 2 * d - 9 : 2 * d
// main function
const luhn = (ds) =>
(sum ([... ds] .map (Number) .reverse () .slice(1) .map (
(d, i) => i % 2 == 0 ? doubleDig (d) : d
)) + Number (last (ds))) % 10 == 0
// sample data
const batch = [
/* Valid */ [4, 5, 3, 9, 6, 7, 7, 9, 0, 8, 0, 1, 6, 8, 0, 8], [5, 5, 3, 5, 7, 6, 6, 7, 6, 8, 7, 5, 1, 4, 3, 9], [3, 7, 1, 6, 1, 2, 0, 1, 9, 9, 8, 5, 2, 3, 6], [6, 0, 1, 1, 1, 4, 4, 3, 4, 0, 6, 8, 2, 9, 0, 5], [4, 5, 3, 9, 4, 0, 4, 9, 6, 7, 8, 6, 9, 6, 6, 6],
/* Invalid */ [4, 5, 3, 2, 7, 7, 8, 7, 7, 1, 0, 9, 1, 7, 9, 5], [5, 7, 9, 5, 5, 9, 3, 3, 9, 2, 1, 3, 4, 6, 4, 3], [3, 7, 5, 7, 9, 6, 0, 8, 4, 4, 5, 9, 9, 1, 4], [6, 0, 1, 1, 1, 2, 7, 9, 6, 1, 7, 7, 7, 9, 3, 5], [5, 3, 8, 2, 0, 1, 9, 7, 7, 2, 8, 8, 3, 8, 5, 4],
/* Mystery */ [3, 4, 4, 8, 0, 1, 9, 6, 8, 3, 0, 5, 4, 1, 4], [5, 4, 6, 6, 1, 0, 0, 8, 6, 1, 6, 2, 0, 2, 3, 9], [6, 0, 1, 1, 3, 7, 7, 0, 2, 0, 9, 6, 2, 6, 5, 6, 2, 0, 3], [4, 9, 2, 9, 8, 7, 7, 1, 6, 9, 2, 1, 7, 0, 9, 3], [4, 9, 1, 3, 5, 4, 0, 4, 6, 3, 0, 7, 2, 5, 2, 3],
]
// demo
console.log (batch .map (luhn))
console .log (
luhn ('4539677908016808'),
luhn ('4532778771091795')
)
.as-console-wrapper {max-height: 100% !important; top: 0}
This function works on the array of single-digit numbers supplied, but also on a string of digits, so that luhn ('4539677908016808') is equivalent to luhn ([4, 5, 3, 9, 6, 7, 7, 9, 0, 8, 0, 1, 6, 8, 0, 8]).
First we ensure we are working with an array of numbers, with [... ds] .map (Number). Then we .reverse the array to make it easier to track even and odd positions without fiddling with the array length. We .slice off the now-first element, which we will only need later as the check-digit. Now we map over the results, doubling the even digits and casting out nines as necessary (using the helper function doubleDig), but keeping the odd digits intact. We total the results using the helper sum and find the last digit using the helper last, convert it into a number and add it to that total. We finish by taking the modulus base 10, and report whether that value is 0.
Those helper functions are useful, and I would almost always prefer to work with such, but each is called in only one place in our main function, which makes it easy to inline them if we want, and we could write a stand-alone version of this doing so:
const luhn = (ds) =>
([...ds] .map (Number) .reverse () .slice(1) .map (
(d, i) => i % 2 == 0 ? (2 * d > 9 ? 2 * d - 9 : 2 * d) : d
) .reduce ((a, b) => a + b, 0) + Number (ds [ds .length - 1])) % 10 == 0
I do not think this is an improvement. While the original version is dense, it's also not hard to follow. This one, especially where we inline the conditional expression (ternary) of doubleDig inside another ternary, seems pretty ugly. Perhaps a more spacious layout would help.1 But sum (...) is definitely cleaner than (...) .reduce ((a , b) => a + b, 0) and last is cleaner than its alternative. Overall, this is uninspiring. But it's good to recognize as an alternative to the helper-function break-down above.
1 Yes, a more spacious layout improves this a lot!
const luhn = (ds) =>
([...ds]
.slice (0, -1)
.reverse ()
.map (Number)
.map ((d, i) => i % 2 == 0 ? (2 * d > 9 ? 2 * d - 9 : 2 * d) : d)
.reduce ((a, b) => a + b, 0)
+ Number (ds [ds .length - 1]))
% 10
== 0
But even better is realizing that we don't have to treat the check-digit separately if we change the parity on which we're doing the digit-doubling routine. That leaves us the much nicer
const luhn = ([...ds]) => (ds
.reverse ()
.map (Number)
.map ((d, i) => i % 2 == 1 ? (2 * d > 9 ? 2 * d - 9 : 2 * d) : d)
.reduce ((a, b) => a + b, 0)
) % 10 == 0
... at which point the helper functions are, at most, nice to have. This reads fine.

Related

Spot start and end index of values outside specified bounds?

I have 3 arrays of size n :
let values = [5, 5, 5, 6, 7, 6, 5, 4, 3, 3, 4, 5, 5];
let min_arr = [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3];
let max_arr = [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7];
let n = values.length;
I am trying to spot the indexes of the start and end of the "zones" where min_arr[i] > values[i] or max_arr[i] < values[i].
In the previous arrays the first zone : values[4] = 7 > 6 = max_arr[4] and !(values[5] = 6 > 6 = max_arr[5]) thus start = 4 and end = 5.
In the previous arrays the second zone : values[8] = 3 < 4 = min_arr[8] and values[9] = 3 < 4 = min_arr[9] but !(values[10] = 4 < 3 = min_arr[10]) thus start = 8 and end = 10.
In the previous arrays the expected output would be : [[4, 5], [8, 10]].
Nb: It does not matter if the last end index is greater than n.
Here is my code so far :
let temp = values
.map((e, i) => min_arr[i] > e || max_arr[i] < e ? i : undefined)
.filter((e) => e);
console.log(temp);
Output
[4, 8, 9]
My logic here is to first get all the indexes where the value is not in the bounds before removing consecutive values and adding the end value.
let res = [];
let start = temp[0];
for (let i = 0; i < temp.length; i++) {
if (i + 1 == temp.length) {
res.push([start, temp[i] + 1]);
break;
}
if (temp[i] + 1 != temp[i + 1]) {
res.push([start, temp[i] + 1]);
start = temp[i + 1];
}
}
console.log(res);
Output
[[4, 5], [8, 10]]
As a beginner in JavaScript, I find this method crude and I believe that the whole process can be done in a declarative way. How should I proceed ?
Please don't hesitate to edit this question if it lacks clarity.
Here are not so crude methods, probably they are in declarative way, also with handling some corner-cases. Corner cases i mean in result you are expencting arrays with 2 elements, open index and close index. So if the last element in array is out of bound - still an array with 2 elements will be returned. And at this point im curious about the correct output, probably you have to clarify it a bit. I assumed that indexes shoult be "first index of item that is out of bounds" and "last index of item that is out of bounds", not the "last index + 1"
let values = [5, 5, 5, 6, 7, 6, 5, 4, 3, 3, 4, 5, 5];
let min_arr = [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3];
let max_arr = [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7];
function getZones(values, predicateFn) {
const reduced = values.reduce(
(acc, curr, i) => {
const isMet = predicateFn(curr, i);
if (isMet) {
if (acc._zone) acc._zone[1] = i;
else acc._zone = [i];
} else if (acc._zone) {
// -1 due to current index is ok, previous was not.
acc._zone[1] = i - 1;
acc.result.push(acc._zone);
acc._zone = undefined;
}
return acc;
},
{
_zone: undefined,
result: []
}
);
// Case when zone was opened and not closed.
// Happens for last item only.
const { _zone, result } = reduced;
if (_zone) {
if (_zone.length === 1) {
_zone[1] = _zone[0];
}
result.push(_zone);
}
return result;
}
const res = getZones(values, (curr, i) => curr < min_arr[i] || curr > max_arr[i]);
console.log(JSON.stringify(res));
Test set i checked with:
const testSets = [
{
// Original set
values: [5, 5, 5, 6, 7, 6, 5, 4, 3, 3, 4, 5, 5],
min_arr: [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3],
max_arr: [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7]
// [[4,4],[8,9]]
},
{
// with starting and ending outs
values: [2, 5, 5, 6, 7, 6, 5, 4, 3, 3, 4, 5, 10],
min_arr: [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3],
max_arr: [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7]
// [[0,0],[4,4],[8,9],[12,12]]
},
{
// with starting and ending "wave"
values: [2, 8, 2, 6, 7, 6, 5, 4, 3, 3, 4, 2, 2, 10],
min_arr: [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3],
max_arr: [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7]
// [[0,2],[4,4],[8,9],[11,13]]
},
{
// Completely out of bounds
values: [2, 2, 2, 2, 2, 2, 2, 8, 8, 8, 8, 8, 8, 8],
min_arr: [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3],
max_arr: [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7]
// [[0,13]]
}
];
Also, here is a codesandbox so you can check the correct output and behavior.
Your method will work only for 2 consecutive values , try an example with more consecutive values like [[2, 7], [8, 11]] and check your method .
The solution can be the next:
let values = [5, 5, 5, 6, 7, 6, 5, 4, 3, 3, 4, 5, 5];
let min_arr = [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3];
let max_arr = [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7];
const result = values
.map((num, i) => (min_arr[i] > num || max_arr[i] < num ? i : undefined))
.reduce((previousValue, currentValue, currentIndex, array) => {
if ((currentValue || currentValue === 0) && !array[currentIndex - 1]) {
const findNextSibling = array.slice(currentIndex).findIndex(i => !i);
const finalConsecutiveValue =
findNextSibling === -1 || findNextSibling === 0
? 1
: findNextSibling + currentIndex;
previousValue.push([currentValue, finalConsecutiveValue]);
}
return previousValue;
}, []);
console.log('result', result);

Delete all elements from an array A which are present in an array B without using a double loop

So, as an input I have two arrays, A and B. Let's suppose that these are the values inside the two:
A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and B = [1, 3, 5, 7, 9]
After the deletion the array A should be [2, 4, 6, 8, 10].
I have written (Javascript) this functioning algorithm to solve this problem:
for (var i=0; i < A.length; i++) {
for (var j=0; j < B.length; j++) {
if(B[j] == A[i])
A.splice(i, 1) // Removes 1 element of the array starting from position i
}
}
I would like to know, is it possible to solve this problem without using a double loop?
What about this:
let A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ;
const B = [1, 3, 5, 7, 9];
A = A.filter(num => !B.includes(num));
Yes it is. You could use a Set. In terms of Set operations you are computing the difference A \ B.
Using a set which is optimized for lookups in O(1) time will speed up the computing the difference siginificantly from O(n²) when using includes() or double for loop to O(n).
const A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const B = [1, 3, 5, 7, 9]
const setB = new Set(B);
const difference = A.filter(x => !setB.has(x));
console.log(difference);
Maybe that ?
const
A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
, B = [1, 2, 3, 5, 7, 9] // no gaps in 1,2 and 2,3
;
for (let i =0, j=0 ; i < A.length; i++)
{
if (A[i]===B[j]) { A.splice(i--,1); j++ }
}
document.write( JSON.stringify(A) )
or (faster code)
const
A = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
, B = [ 1, 3, 5, 7, 9 ]
;
for (let i = A.length, j= B.length -1 ; i-- ; )
{
if (A[i]===B[j]) { A.splice(i,1); j-- }
}
document.write( JSON.stringify(A) )

convert 9x9 array into 9 (3x3) array in javascript?

convert 9x9 array into 9 (3x3) array in javascript?
i have written the code, but its not pushing the 3x3's into separate array.
i want 9 3x3 arrays
let array =
[
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[2, 3, 1, 5, 6, 4, 8, 9, 7],
[3, 1, 2, 6, 4, 5, 9, 7, 8],
[4, 5, 6, 7, 8, 9, 1, 2, 3],
[5, 6, 4, 8, 9, 7, 2, 3, 1],
[6, 4, 5, 9, 7, 8, 3, 1, 2],
[7, 8, 9, 1, 2, 3, 4, 5, 6],
[8, 9, 7, 2, 3, 1, 5, 6, 4],
[9, 7, 8, 3, 1, 2, 6, 4, 5]
];
let final=[];
let row = [0,1,2];
let col = [0,1,2];
let counter = 0;
for ( let i = 0 ; i <= array.length - 1 ; i += 3 )
{
for(let j = 0 ; j <= array.length - 1 ; j += 3 )
{
final.push([]);
row.forEach( ele1 => {
final[counter].push([])
col.forEach( ele2 => {
final[counter][ele1].push(array[ele1+i][ele2+j]);
})
})
counter+=1;
}
}
console.log(final)
You can loop through the array using map function and check for the index values to break into arrays as required.
let myArr =[[1, 2, 3, 4, 5, 6, 7, 8, 9],
[2, 3, 1, 5, 6, 4, 8, 9, 7],
[3, 1, 2, 6, 4, 5, 9, 7, 8],
[4, 5, 6, 7, 8, 9, 1, 2, 3],
[5, 6, 4, 8, 9, 7, 2, 3, 1],
[6, 4, 5, 9, 7, 8, 3, 1, 2],
[7, 8, 9, 1, 2, 3, 4, 5, 6],
[8, 9, 7, 2, 3, 1, 5, 6, 4],
[9, 7, 8, 3, 1, 2, 6, 4, 5]];
let newArr = convertTo3x3(myArr);
console.log(newArr);
function convertTo3x3(myArr){
let array3x3 = [];
myArr.map((row, rIndex) => {
let tempArr = [];
let row3 = [];
row.map((item, lIndex) => {
// convert each row to 3x3 rows
if(lIndex % 3 == 0){
// reset row3 for new 3x3 arr on every 1st item of 3x3
row3 = [];
}
row3.push(item);
if(lIndex % 3 == 2){
// push 3x3 row to tempArr on every 3rd item of 3x3
tempArr.push(row3);
}
});
array3x3.push(tempArr);
});
return array3x3;
}
Your code seems to work fine, but if you want a more javascriptey code, that performs worse, here you go.
let array = [
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[2, 3, 1, 5, 6, 4, 8, 9, 7],
[3, 1, 2, 6, 4, 5, 9, 7, 8],
[4, 5, 6, 7, 8, 9, 1, 2, 3],
[5, 6, 4, 8, 9, 7, 2, 3, 1],
[6, 4, 5, 9, 7, 8, 3, 1, 2],
[7, 8, 9, 1, 2, 3, 4, 5, 6],
[8, 9, 7, 2, 3, 1, 5, 6, 4],
[9, 7, 8, 3, 1, 2, 6, 4, 5],
];
let final = [];
array
.reduce((a, c) => {
const chunk = (arr, n) =>
arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : [];
return [...a, ...chunk(c, 3)];
}, [])
.map((el, i) => {
if (i % 3 == 0) {
final = [...final, [el]];
} else {
final[Math.floor(i / 3)] = [...final[Math.floor(i / 3)], el];
}
});
console.log(final);
Your code is trying to do too much. Make it difficult to understand.
I would first break it up. Write a simple function than knows how to take an array-of-arrays and return an arbitrary rectangular selection from it. Something like this, that's easy to test:
/**
* Snip a rectangular section of an array of arrays (ragged, 2D array).
* The returned array-of-arrys will ALWAYS be the specified size, padded
* with 'undefined' values to the specified size.
*
* #param {Object[][]} arr - An array of arrays (ragged 2D array)
* #param {number} row - Origin row: {row,col} denotes the upper-left corner of the rectangle to snip
* #param {number} col - Origin column: {row,col} denotes the upper-left corner of the rectangle to snip
* #param {number} nrows - Number of rows to snip
* #param {number} ncols - Nunber of columns to snip
*
* #returns {Object[][]}
*/
function snip(arr, row, col, nrows, ncols ) {
const selection = [];
for ( let r = row, i = 0 ; r < row+nrows ; ++r, ++i ) {
selection[i] = [];
const tmp = arr[r] ?? [];
for ( let c = col, j = 0 ; c < col+ncols ; ++c, ++j ) {
selection[i][j] = tmp[c];
}
}
return selection;
}
Once you have that, then chopping up your larger array-of-arrays into 3x3 arrays is easy.
This code starts at the top left corner or your 9x9 array and returns a flat list containing 9 separate 3x3 arrays, chopped out left-to-right and top-to-bottom:
final = [];
for ( let x = 0 ; x < 9; x += 3 ) {
for ( let y = 0 ; y < 9 ; y += 3 ) {
// (x,y) denotes the top left corner of the desired sub-array
final.push( snip(arr, x,y, 3,3 ) );
}
}
The nice thing about this approach is that it is easy to test, and
.
.
.
It's flexible. It can handle a source array of any size, and you can chop it up into subarrays of any size and in any order, whatever you see fit to do.

Chunk data from array of arrays

Before marking this as answered by another question please note this is an array of arrays, not a flat array, also, the number I have given are an example, I have just shown them so you can visually see what is happening.
I am trying to loop through an array of arrays.
I have the following array:-
[
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[3, 2, 1, 6, 5, 4, 9, 8, 7],
[6, 5, 4, 9, 8, 7, 3, 2, 1],
[7, 8, 9, 3, 2, 1, 6, 5, 4]
]
How is it possible to transform this array into chunks of 3x3 for example:-
[
[1, 2, 3, 1, 2, 3, 1, 2, 3],
[4, 5, 6, 4, 5, 6, 4, 5, 6],
[7, 8, 9, 7, 8, 9, 7, 8, 9],
[3, 2, 1, 6, 5, 4, 9, 8, 7],
[6, 5, 4, 9, 8, 7, 3, 2, 1],
[7, 8, 9, 3, 2, 1, 6, 5, 4],
]
As you can see from the array above I have chunked it using the first 3 values from each array and then by the 2nd 3n from each array and lastly the final 3n from each array.
So the array would be chunked like the following:-
1 2 3 | 4 5 6 | 7 8 9
1 2 3 | 4 5 6 | 7 8 9
1 2 3 | 4 5 6 | 7 8 9
---------------------
3 2 1 | 6 5 4 | 9 8 7
6 5 4 | 9 8 7 | 3 2 1
7 8 9 | 3 2 1 | 6 5 4
I have tried to loop through each line and resetting the column count when it hits an increment and increasing the row but this didn't work.
I can update the question with previous attempts if this is of any help?
Also just a note, the array will be different sizes but always divisible by a particular number, for the above example I have chosen the number 3.
I have updated the question with more information.
The array of arrays will always be divisible by a specific number, this example shows a divisible number of 3.
This can be solved with a chunk as per this question Split array into chunks combined with a zip as per this Javascript equivalent of Python's zip function
This has the benefit of being able to reverse the operation to get back to the original.
/** #see: https://stackoverflow.com/questions/8495687/split-array-into-chunks */
function chunk(array, chunk) {
let result = [];
for (let i = 0; i < array.length; i += chunk) {
result.push(array.slice(i, i + chunk));
}
return result;
}
/** #see: https://stackoverflow.com/questions/4856717/javascript-equivalent-of-pythons-zip-function */
function zip(...rows) {
return [...rows[0]].map((_, c) => rows.map((row) => row[c]));
}
const array = [
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[3, 2, 1, 6, 5, 4, 9, 8, 7],
[6, 5, 4, 9, 8, 7, 3, 2, 1],
[7, 8, 9, 3, 2, 1, 6, 5, 4],
];
const result = chunk(array, 3)
.flatMap((innerChunk) =>
zip(...innerChunk.map((arr) => chunk(arr, 3)))
.map((arr) => arr.flat())
);
console.log(result.map((a) => a.join(', ')));
// Allows the result to be reverted to the original
const reversed = chunk(result, 3)
.flatMap((innerChunk) =>
zip(...innerChunk.map((arr) => chunk(arr, 3)))
.map((arr) => arr.flat())
);
console.log(reversed.map((a) => a.join(', ')));
You can do it with a nested loop, slicing the array each time based on the size of the outer array.
const arr = [
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9]
];
let out = [];
for(let i in arr) { out.push([]);}
arr.forEach((e, i) => {
let scale = e.length / arr.length;
for(let j in arr)
{
out[j] = out[j].concat(e.slice(j * scale, j * scale + scale));
}
});
console.log(out);
Here it is once more with your original array prior to your edit:
const arr = [
[1, 3, 2, 5, 2, 4, 3, 6, 8],
[1, 4, 3, 6, 7, 3, 6, 4, 5],
[2, 4, 1, 4, 6, 3, 7, 9, 7]
];
let out = [];
for(let i in arr) { out.push([]);}
arr.forEach((e, i) => {
let scale = e.length / arr.length;
for(let j in arr)
{
out[j] = out[j].concat(e.slice(j * scale, j * scale + scale));
}
});
console.log(out);

How can I change the position of a particular item in an array?

I'm trying to change an item's index position in an array, but I cannot figure out a way.
{
"items": [
1,
3,
2
]
}
You can use splice to move an element in an array:
var arr = [
1,
3,
2
];
var oldIndex = 2,
newIndex = 1;
arr.splice(newIndex, 0, arr.splice(oldIndex, 1)[0]);
This makes [1, 2, 3]
The internal splice removes and returns the element, while the external one inserts it back.
Just for fun I defined a generic function able to move a slice, not just an element, and doing the computation of the index:
Object.defineProperty(Array.prototype, "move", {
value:function(oldIndex, newIndex, nbElements){
this.splice.apply(
this, [newIndex-nbElements*(newIndex>oldIndex), 0].concat(this.splice(oldIndex, nbElements))
);
}
});
var arr = [0, 1, 2, 7, 8, 3, 4, 5, 6, 9];
arr.move(5, 3, 4);
console.log('1:', arr) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
var arr = [0, 1, 2, 7, 8, 3, 4, 5, 6, 9];
arr.move(3, 9, 2);
console.log('2:', arr); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
var arr = [0, 1, 2, 4, 5, 3, 6, 7];
arr.move(5, 3, 1);
console.log('3:', arr); // [0, 1, 2, 3, 4, 5, 6, 7]
var arr = [0, 3, 1, 2, 4, 5, 6, 7];
arr.move(1, 4, 1);
console.log('3:', arr); // [0, 1, 2, 3, 4, 5, 6, 7]
JS Bin
If you want to sort them in Unicode order (where numbers become strings) you can use sort() function.
items.sort();
If you have your custom order, you need to provide a sort function to the sort function.
function compare(a, b) {
if (a is less than b by some ordering criterion) {
return -1;
}
if (a is greater than b by the ordering criterion) {
return 1;
}
// a must be equal to b
return 0;
}
and you use it like this:
items.sort(compare(a, b));

Categories