I am looking for a Javascript Algorithm to split an array into chunks, but avoiding any small left overs. For example:
_.chunk([1, 2, 3, 4, 5, 6, 7], 3) // [[1, 2, 3], [4, 5, 6], [7]]
But I want this:
_.chunk([1, 2, 3, 4, 5, 6, 7], 3) // [[1, 2, 3], [4, 5], [6, 7]]
_.chunk([1, 2, 3, 4, 5, 6, 7], 4) // [[1, 2, 3, 4], [5, 6, 7]]
_.chunk([1, 2, 3, 4, 5, 6, 7], 5) // [[1, 2, 3, 4], [5, 6, 7]]
_.chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3) // [[1, 2, 3], [4, 5, 6], [7, 8], [9, 10]]
So basically the output is spread over several arrays with a maximum number of elements passed in as second argument.
You should recalculate the size, which might need to be smaller than given. Then calculate from where the size should be one less for the remaining chunks.
So you will have potentially two different chunk sizes (which differ by 1). For both you can call the original _.chunk:
function chunk(arr, size) {
const count = Math.ceil(arr.length / size);
size = Math.ceil(arr.length / count);
const i = arr.length-(size-1)*(arr.length%size && size-(arr.length%size));
return _.chunk(arr.slice(0, i), size).concat(
_.chunk(arr.slice(i), size-1));
}
for (let i = 1; i < 9; i++) {
console.log(i, JSON.stringify(chunk([1, 2, 3, 4, 5, 6, 7], i)));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script>
This is done without lodash, it generates an array of proper length then fills in the parts with slices of the input array. It's only a few lines of code, depending how you count.
The caveat is the slices are up to how the decimals round. For your last example, you want to chunk a 10-length array with a limit of 3 per chunk. Well, dividing it out, you'll get the output:
[ [1,2], [3,4,5], [6,7], [8,9,10] ]
and not what you wanted:
[ [1,2,3], [4,5,6], [7,8], [9,10] ]
For most applications I don't think it matters much. I'm using this to chunk large inputs to an API that is throttled.
function chunk(array, limit) {
const numChunks = Math.ceil(array.length / limit);
return Array.from(
{ length: numChunks },
(_, i) => array.slice(i * array.length / numChunks, (i + 1) * array.length / numChunks)
);
}
console.log(chunk([1, 2, 3, 4, 5, 6, 7], 3));
console.log(chunk([1, 2, 3, 4, 5, 6, 7], 4));
console.log(chunk([1, 2, 3, 4, 5, 6, 7], 5));
console.log(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3));
An alternative solution uses recursion to chunk the "leftover" array. This one meets your criteria.
function chunk(array, limit) {
if (array.length <= limit) return [array];
const perChunk = Math.ceil(array.length / Math.ceil(array.length / limit));
return [array.slice(0, perChunk)].concat(chunk(array.slice(perChunk), limit));
}
console.log(chunk([1, 2, 3, 4, 5, 6, 7], 3));
console.log(chunk([1, 2, 3, 4, 5, 6, 7], 4));
console.log(chunk([1, 2, 3, 4, 5, 6, 7], 5));
console.log(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3));
Related
I'm doing a codewars challenge that checks if Sudoku grid is valid.
https://www.codewars.com/kata/529bf0e9bdf7657179000008/train/javascript
It returns false if there are duplicate values in 1) horizontal line, 2) vertical line, 3) 3x3 block, and 4) incomplete (if there are 0s). I'm stuck on checking the horizontal line.
I am using the values array to store the numbers then using the for in loop to check if it's a duplicate. If it is, return false. The problem in the code is checking the first horizontal line [5, 3, 4, 6, 7, 8, 9, 1, 2]. It returns false when checking 1 and I don't know why.
function validSolution(board) {
let valid = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// testing for 0's
for (let i = 0; i < board.length; i++) {
for (let j = 0; j < board[i].length; j++) {
if (board[i][j] === 0) {
return false;
}
}
}
// testing horizontal line
for (let i = 0; i < board.length; i++) {
values = [];
for (let j = 0; j < board[i].length; j++) {
console.log(values);
console.log(board[i][j]);
if (board[i][j] in values) { // problem is here once the value reaches 1 on the first horizontal line.
console.log(board[i][j]);
return false;
} else {
values.push(board[i][j]);
}
}
return true;
}
}
console.log( // this array is a valid board and should return true.
validSolution([
[5, 3, 4, 6, 7, 8, 9, 1, 2],
[6, 7, 2, 1, 9, 5, 3, 4, 8],
[1, 9, 8, 3, 4, 2, 5, 6, 7],
[8, 5, 9, 7, 6, 1, 4, 2, 3],
[4, 2, 6, 8, 5, 3, 7, 9, 1],
[7, 1, 3, 9, 2, 4, 8, 5, 6],
[9, 6, 1, 5, 3, 7, 2, 8, 4],
[2, 8, 7, 4, 1, 9, 6, 3, 5],
[3, 4, 5, 2, 8, 6, 1, 7, 9],
])
);
I'm console logging both the element in the index and the value. Here is the result:
(0) []
5
(1) [5]
3
(2) [5, 3]
4
(3) [5, 3, 4]
6
(4) [5, 3, 4, 6]
7
(5) [5, 3, 4, 6, 7]
8
(6) [5, 3, 4, 6, 7, 8]
9
(7) [5, 3, 4, 6, 7, 8, 9]
1
1
false
I think Boolean algebra should not be underestimated
function check1to9valid( arrN )
{
let binVal = 0;
for (let v of arrN)
{
let bin = 2 ** v // Number(v)
if (binVal & bin) { binVal = 0; break; } // avoid duplicates
else { binVal |= bin; }
}
return (binVal === 0b1111111110 )
}
const validGrid =
[ [ 5, 3, 4, 6, 7, 8, 9, 1, 2 ]
, [ 6, 7, 2, 1, 9, 5, 3, 4, 8 ]
, [ 1, 9, 8, 3, 4, 2, 5, 6, 7 ]
, [ 8, 5, 9, 7, 6, 1, 4, 2, 3 ]
, [ 4, 2, 6, 8, 5, 3, 7, 9, 1 ]
, [ 7, 1, 3, 9, 2, 4, 8, 5, 6 ]
, [ 9, 6, 1, 5, 3, 7, 2, 8, 4 ]
, [ 2, 8, 7, 4, 1, 9, 6, 3, 5 ]
, [ 3, 4, 5, 2, 8, 6, 1, 7, 9 ]
]
let isValid = true
// testing horizontal lines
for (let arrLine of validGrid )
{
if (!check1to9valid( arrLine ))
{
isValid = false;
break;
}
}
console.log('all grid\'s lines are valid :', isValid )
// testing vertical lines
isValid = true
for (let i=0; i< 9; i++ )
{
if (!check1to9valid( validGrid.map(line=>line[i]) ))
{
isValid = false;
break;
}
}
console.log('all grid\'s columns are valid : ', isValid )
I am doing a project for codecademy and am a little stuck on the syntax among other things ;). I am trying to reverse iterate an array and as I iterate to the left I need to double every other digit THEN check to see whether those numbers are greater than 9 after doubling them. THEN subtract the doubled number by 9. Any help would be much appreciated! This is my code so far
// 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) {
for (let i = arr.length - 2; i >= 0; i--)
if(i % 2 === 0) {
arr[i] = arr[i] * 2;
} else if (i % 2 === 1) {
arr[i]
}
}
arr[i] by itself doesn't do anything, so you can just leave out the else statemennt.
After doubling the number, use an if statement to check if it's higher than 9.
function validateCred(arr) {
for (let i = arr.length - 2; i >= 0; i--) {
if (i % 2 === 0) {
arr[i] *= 2;
if (arr[i] > 9) {
arr[i] -= 9;
}
}
}
}
function validSolution(board){
for(i=0;i<board.length;i++){
if (new Set(board[i]).size != 9) return false
//if (new Set(getVertical(i,board)).size != 9) return false
}
return true
}
let getVertical = (num,board) => {
let result = []
for(i=0;i<board.length;i++){
result.push(board[i][num])
}
console.log(result)
return result
}
I am working on sudoku checker if I am adding the commented line of code the tests that were passing are failing :( like the first 'if' statement disappeared
sample input (should be false) and it is until adding second 'if' statement
[[5, 3, 4, 6, 7, 8, 9, 1, 2],
[6, 7, 2, 1, 9, 0, 3, 4, 8],
[1, 0, 0, 3, 4, 2, 5, 6, 0],
[8, 5, 9, 7, 6, 1, 0, 2, 0],
[4, 2, 6, 8, 5, 3, 7, 9, 1],
[7, 1, 3, 9, 2, 4, 8, 5, 6],
[9, 0, 1, 5, 3, 7, 2, 1, 4],
[2, 8, 7, 4, 1, 9, 6, 3, 5],
[3, 0, 0, 4, 8, 1, 1, 7, 9]]
The propblem is that you declare variable i without let keyword.
That means i is a global variable, so in getVertical function i store value 8 and main loop in validSolution skip to end and returns true
Is there a way to call the names of the arrays in an array of arrays? I am applying a Luhn algorithm to a set of credit card numbers in an array of arrays I would like output that says, 'Array xxx is true/false.' I have created a workaround below by creating an array of "names" but can the batch array names be accessed directly?
// 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]
const arrNames = ['valid1', 'valid2', 'valid3', 'valid4', 'valid5', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5', 'mystery1', 'mystery2', 'mystery3', 'mystery4', 'mystery5']
const batchL = batch.length;
function ValidateCred(CC) {
if (CC.length > 19) return (false);
sum = 0;
mul = 1;
l = CC.length-1;
for (i = 0; i < l+1; i++) {
digit = CC[l - i];
tproduct = digit * mul;
if (tproduct >= 10) sum += (tproduct % 10) + 1;
else sum += tproduct;
if (mul == 1) mul++;
else mul--;
};
if ((sum % 10) == 0) return (true);
else return (false);
};
for (b = 0; b < batchL; b++) {
console.log('Card', arrNames[b],'is: ', ValidateCred(batch[b]));
};
Probably the best approach to this problem is to use an object instead of an array:
const validCCNumbers = {
valid1: [4, 5, 3, 9, 6, 7, 7, 9, 0, 8, 0, 1, 6, 8, 0, 8],
valid2: [5, 5, 3, 5, 7, 6, 6, 7, 6, 8, 7, 5, 1, 4, 3, 9],
valid3: [3, 7, 1, 6, 1, 2, 0, 1, 9, 9, 8, 5, 2, 3, 6],
valid4: [6, 0, 1, 1, 1, 4, 4, 3, 4, 0, 6, 8, 2, 9, 0, 5],
valid5: [4, 5, 3, 9, 4, 0, 4, 9, 6, 7, 8, 6, 9, 6, 6, 6]
}
If you need to access the names of each array you can do it with Object.keys(validCCNumbers)
You can shorten the whole operation considerably:
const all= {valid1:["4539677908016808", "5535766768751439", "371612019985236", "6011144340682905", "4539404967869666"],
invalid1:["4532778771091795", "5795593392134643", "375796084459914", "6011127961777935", "5382019772883854"],
mystery1:["344801968305414", "5466100861620239", "6011377020962656203", "4929877169217093", "4913540463072523"]};
Object.entries(all).forEach(([grname,arr])=>{
console.log(grname);
console.log(arr.map(cc=>cc+": "+ValidateCred(cc)));
});
function ValidateCred(ccstr) {
if (ccstr.length > 19) return (false);
let CC=ccstr.split(""), digit, tproduct,
sum = 0, mul = 1, l = CC.length-1;
for (i = 0; i < l+1; i++) {
digit = CC[l - i];
tproduct = digit * mul;
if (tproduct >= 10) sum += (tproduct % 10) + 1;
else sum += tproduct;
if (mul == 1) mul++;
else mul--;
};
return sum % 10 == 0
};
For example let's say I have arrays
a = [1, 2, 3, 4, 5, 6] and b = a
And the output I expect would be:
1 + 1, 1 + 2, 1 + 3 ... 3 + 1, 3 + 2, 3 + 4 ... 6 + 3, 6 + 4, 6 + 5, 6 + 6
I would prefer to make this simple calculation in JS or Ruby, but I don't mind answer in any other language. Can anyone provide me any direction?
In Ruby:
a.product(b).map {|p| p.reduce(:+) }.uniq
a = [1, 2, 3, 4, 5, 6]
b = [1, 4, -1, 7, 9]
a.product(b).map { |a,b| a+b }.uniq
#=> [2, 5, 0, 8, 10, 3, 6, 1, 9, 11, 4, 7, 12, 13, 14, 15]
The steps:
c = a.product(b)
#=> [[1, 1], [1, 4], [1, -1], [1, 7], [1, 9],
# [2, 1], [2, 4], [2, -1], [2, 7], [2, 9],
# [3, 1], [3, 4], [3, -1], [3, 7], [3, 9],
# [4, 1], [4, 4], [4, -1], [4, 7], [4, 9],
# [5, 1], [5, 4], [5, -1], [5, 7], [5, 9],
# [6, 1], [6, 4], [6, -1], [6, 7], [6, 9]]
d = c.map { |a,b| a+b }
#=> [2, 5, 0, 8, 10,
# 3, 6, 1, 9, 11,
# 4, 7, 2, 10, 12,
# 5, 8, 3, 11, 13,
# 6, 9, 4, 12, 14,
# 7, 10, 5, 13, 15]
d.uniq
#=> [2, 5, 0, 8, 10, 3, 6, 1, 9, 11, 4, 7, 12, 13, 14, 15]
In javascript, get unique sums using Set
var a = [1, 2, 3, 4, 5, 6];
var r = new Set();
a.forEach(x => a.forEach(y => r.add(x + y)))
document.write('<pre>' + Array.from(r) + '</pre>');
You can use Array.prototype.forEach() for the iteration over the arrays.
The forEach() method executes a provided function once per array element.
The result is without repeat.
function xSums(array) {
var r = [],
o = {};
array.forEach(function (a) {
array.forEach(function (b) {
if (!o[a + b]) {
r.push(b + a);
o[a + b] = true;
}
});
});
return r;
}
document.write('<pre>' + JSON.stringify(xSums([1, 2, 3, 4, 5]), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(xSums([3, 7, 42]), 0, 4) + '</pre>');
I would do something like this:
a = [1, 2, 3, 4, 5, 6]
a.permutation(2).map { |x, y| x + y }
Try looping through both arrays, adding the first to the second, and storing the results in a third, eg;
var one = [1, 2, 3];
var two = [4, 5, 6];
var three = [];
for(var x = 0; x < one.length; ×++){
for(var y = 0; y < two.length; y++){
three.push(one[x] + two[y]);
}
}
This would result in three[0] = 1 + 4, three[1] = 1+ 5, three[2] =1+ 6, three[3] = 2 + 4, three[4] = 2+ 5 etc...