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);
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.
what's up? I hope you going well.
So, my question is, I have a array with number that I have to compare with another arrays (like 1 to X), what is the best way to:
1º compare the arrays and retrieve the numbers that are equals.
2º the numbers of elements that are equal (without using .length on the array with numbers are equals).
Example:
Array 1 = [1, 3, 4, 5, 6, 7, 8, 9, 11, 13, 16]
Array 2 = [1, 2, 3, 7, 9, 12, 16, 17]
That way, the total numbers is 5
And the numbers are: [1, 3, 7, 9, 16]
My method is using forEach and compare each item and using .length on the array with the numbers that are equals, there's another way or best way to do this?
Another example using more arrays:
Arr1 = [1, 2, 3, 5, 6, 7, 8, 10, 15]
Arr2 = [
[1, 3, 5, 7, 8, 9, 10, 11, 12],
[2, 5, 6, 7, 9, 10],
[1, 3, 5, 7, 10, 11, 13, 14, 15]
]
// Output
6, [1, 3, 5, 7, 8, 10]
5, [2, 5, 6, 7, 10]
7, [1, 3, 5, 6, 7, 10, 15]
Thanks for the answer.
I like using Set for this purpose. You can create a Set from your first array and then any lookup in that Set (using Set.has) is O(1) efficiency.
const arr1 = [1, 3, 4, 5, 6, 7, 8, 9, 11, 13, 16];
const arr2 = [1, 2, 3, 7, 9, 12, 16, 17];
const arr1Items = new Set(arr1);
const matched = arr2.filter(el => arr1Items.has(el));
console.log(matched.length, matched);
Arr2 is an array, not an object, your code would change accordingly
Arr1 = [1, 2, 3, 5, 6, 7, 8, 10, 15]
Arr2 = [
[1, 3, 5, 7, 8, 9, 10, 11, 12,],
[2, 5, 6, 7, 9, 10],
[1, 3, 5, 7, 10, 11, 13, 14, 15,]
]
Given that, the solution is a oneliner:
res = Arr2.map(a=>a.filter(x=>Arr1.indexOf(x)!=-1).length)
It should be straightforward but, just in case:
The [].indexOf(el) method give you the position of the parameter in the array, if that element is not present it will return -1. Therefore, the function
x => Arr1.indexOf(x)!=-1
returns true or false if x is present or not in the Arr1 array
The [].filter(fn) method use the fn function to evaluate every array element and give as result an array with the evaluated true elements.
a.filter(x => Arr1.indexOf(x)!=-1)
Means give me all the elements of array a presents in Arr1
Now we just have to count the lenght of that array
a.filter(x => Arr1.indexOf(x)!=-1).length
and pass this count to the [].map(fn) method to have the result we need.
The function I wrote below will give the results you want, but remember the function returns an array of arrays, even if the second parameter had only one array or was an array of elements instead of array of arrays (Works for both).
Arr1 = [1, 2, 3, 5, 6, 7, 8, 10, 15]
Arr2 = [
[1, 3, 5, 7, 8, 9, 10, 11, 12],
[2, 5, 6, 7, 9, 10],
[1, 3, 5, 7, 10, 11, 13, 14, 15]
]
Arr3 = [1, 5, 6, 9, 12, 15, 17]
function check(base_array,search_values)
{
if(base_array.length===0 || search_values.length===0)
{
return [];
}
else if(Array.isArray(search_values[0]))// Check if second parameter is an array of arrays.
{
var result=[];
search_values.forEach(search=>{
var result_sub=[];
search.forEach(key=>{
if(base_array.includes(key))
{
result_sub.push(key);
}
});
result.push(result_sub);
});
return result;
}
else
{
var result=[];
search_values.forEach(key=>{
if(base_array.includes(key))
{
result.push(key);
}
});
return [result];
}
}
console.log("Array of Arrays");
console.log(check(Arr1,Arr2));
console.log("Array of Elements");
console.log(check(Arr1,Arr3));
From the returned result you can loop through the value to get the elements and the number of elements by checking length of array.
result.forEach(element=>{
console.log(result.length, result);// number of elements doesn't have to be passed
});
What the Function does is it checks if any array is empty , then returns empty array [], if the second array is an array of arrays it loops through each array and then to each element in the sub array and checks if it exists in the first array, else if the array was array of elements, then it just loops through the elements and checks if it exists in the first array. And returns the result stored