I am trying to make a sudoku generator on a 4 by 4 grid in JavaScript.
So I declare a matrix of objects such as {number: 0, cbd: 1}, which represent the number that is going to be on the cell, and a state variable that controls if that cell can later be dug (cbd):
var matrix = new Array(4).fill({number: 0, cbd: 1}).map(() => new Array(4).fill({number: 0, cbd: 1}));
I then used this function to fill the grid, respecting all the 3 sudoku restraints: no repetitions on row, columns and square:
fillmatrix4(matrix,tam){
var numberList = [...Array(4+1).keys()].slice(1); //[1,2,3,4]
for(var k = 0; k < 4**2; k++){
var row = Math.floor(k/4);
var col = k%4;
if (matrix[row][col].number == 0){
numberList.sort(() => Math.random() - 0.5); //shuffles the array
for(var q = 0; q < numberList.length; q++){
var ids = matrix[row].map(a => a.number); //creates an array with the number property from all cells in that row
if (!(ids.includes(numberList[q]))){ //check if number is in row
if ((matrix[0][col].number != numberList[q]) && (matrix[1][col].number != numberList[q]) && (matrix[2][col].number != numberList[q]) && (matrix[3][col].number != numberList[q])){ //check if number is in column
var square = [];
if (row<2){It is used to find out what subgrid we are in
if (col<2){
square = [matrix[0][0].number, matrix[0][1].number, matrix[1][0].number, matrix[1][1].number];
} else {
square = [matrix[0][2].number, matrix[0][3].number, matrix[1][2].number, matrix[1][3].number];
}
} else {
if (col<2){
square = [matrix[2][0].number, matrix[2][1].number, matrix[3][0].number, matrix[3][1].number];
} else {
square = [matrix[2][2].number, matrix[2][3].number, matrix[3][2].number, matrix[3][3].number];
}
}
if (!(square.includes(numberList[q]))){ //check if number is that subgrid
matrix[row][col].number = numberList[q]; //if number not in row, column and square, adds it to matrix
if (this.checkGrid(matrix,tam)){ //returns true all matrix is filled
return matrix;
}
}
}
}
}
}
}
I know for a fact that this function works. I tested it on a matrix of integers, but I need that each individually cell has that variable cbd.
But now, with the matrix of objects above declared, it's not "shuffling". The result is always a grid filled like this:
a | a | a | a
b | b | b | b
c | c | c | c
d | d | d | d
where a,b,c,d are numbers from 1 to 4.
What might be the reason for this?
When you do:
Array(4).fill({number: 0, cbd: 1})
this fills every entry with a reference to the same object in memory. Graphically, it looks like:
.-----------.
| number: 0 |
| cbd: 1 |
`-----------`
^ ^ ^ ^
| | | |
variable `matrix`: [0, 1, 2, 3]
Changes made on any of these array indices will mutate the same underlying object.
Here's a minimal, complete, reproducible example:
const arr = Array(4).fill({foo: "bar"});
arr[0].foo = "baz";
console.log(JSON.stringify(arr, null, 2));
console.log(arr);
You can see all 4 entries reflect the change made to the first object. Note that the stack snippet console.log of the array shows "ref" to an object id for 3 of the 4 buckets.
To fix the problem, use .map to generate distinct objects:
const arr = Array(4).fill().map(() => ({foo: "bar"}));
arr[0].foo = "baz";
console.log(JSON.stringify(arr, null, 2));
console.log(arr);
Or, in your code:
const matrix = Array(4).fill().map(() => ({number: 0, cbd: 1}))
.map(() => Array(4).fill().map(() => ({number: 0, cbd: 1})));
console.log(matrix);
(the new is unnecessary).
As an aside, try to avoid deeply nested blocks. Consider writing helper functions to handle some of this logic and make the code easier to follow.
Also, if you're looking for good shuffling in JS, I recommend looking into the Fisher-Yates shuffle.
Related
I'm wanting to add a horizontal check for a winning move in my connect 4 game - However I'm completely flumoxed with how to proceed.
There are already working checks for horizontal and vertical.
You can see that we are checking with a regex test against a stringified version of the array matrix.
A winning vertical array looks like this:
Array and joined array passed to the test function:
A winning horiontal array looks like this:
Array and joined array passed to the horizontal test function:
A winning diagonal array looks like this:
const testWin = (arr: number[]): boolean => /1{4}|2{4}/.test(arr.join(""));
const usePlayPiece = () => {
const [board, setBoard] = useState(boardState);
const [player, setPlayerTurn] = useState(playerState);
const [gameOver, setGameOver] = useState(gameOverState);
return (col: number) => {
// Play piece (non mutating)
const newBoard = board.map((column, i) => (i === col) ? [...column, player] : column);
const row = newBoard[col].length - 1;
if (
testWin(newBoard[col]) // Did win vertically
|| testWin(newBoard.map((col) => col[row] || 0)) // Did win horizontally
// || testWin() // Did win diagonally
) {
setGameOver(true);
} else {
// Keep playing
}
setBoard(newBoard);
};
};
You could temporarily pad your columns with zeroes so they are equally sized (typically to length 6). Then turn that 2D array to a string, where the columns are separated with an extra character, and then use a one-for-all regular expression on it:
const testWin = (arr: number[][]): boolean => =>
/([12])(\1{3}|(.{5}\1){3}|(.{6}\1){3}|(.{7}\1){3})/.test(
arr.map(col => col.join("").padEnd(6, "0")).join(",")
);
And just have one call where you pass the complete board as argument:
if (testWin(newBoard)) {
// game over...
}
Keeping in with the string version of your array, you need to test the full matrix through so you will need to pad out numbers after the last entry in all columns. This will allow you to test for a consistent pattern.
Using a 6 * 7 array you would end up with 6 numbers between each number you are testing for. The regex then will look like
1\d{6}1\d{6}1\d{6}1|2\d{6}2\d{6}2\d{6}2
For the opposite direction you will swap the {6} for {4}.
This can be simplified to 1(\d{6}1){3}|2(\d{6}2){3} and 1(\d{4}1){3}|2(\d{4}2){3}
Apart from operating on strings (which I think might be faster for very large matrices), the problem can be generalized on m x n matrices:
function genMtrx (n,m, probEmp = 0.8){
return Array.from({length:n}).map(d =>
Array.from({length:m}).map(d =>
Math.random() < probEmp ? 0 : Math.random() < 0.5 ? 1 : 2
)
)
}
Normally there are 8 directions you can go (l,r,t,b,tr,tl,bl,br), but since you are testing every cell until you hit a winning condition, you would need 4 of the directions like the others posted here:
const dirs = {
right: (i,j) => [i+1, j],
top: (i,j) => [i, j+1],
topleft: (i,j) => [i-1,j+1],
topright: (i,j) => [i+1,j+1]
}
Then based on these dirs, you can generalize a winning condition:
function isWinning(mtrx, nr = 1){
let streak = 0,
nCol = mtrx.length,
nRow = mtrx[0].length,
dirKeys = Object.keys(dirs),
dirKeysL = dirKeys.length;
for (let i = 0; i < nCol; ++i){
for (let j = 0; j < nRow; ++j) {
for (let k = 0; k < dirKeysL; ++k) {
let dir = dirs[dirKeys[k]],
[nI, nJ] = dir(i,j),
cell = mtrx[nI]?.[nJ];
while (cell && cell === nr) {
if(++streak === 4) {
return true;
}
[nI, nJ] = dir(nI, nJ);
cell = mtrx[nI]?.[nJ];
}
streak = 0;
}
}
}
return false;
}
The function default tests for opponent1, but if you pass 2, it will test that one instead. Then you can do (for opponent 2):
isWinning(genMtrx(100,100,0.9),2)
I am sure the algorithm can be optimized much further. I also think if the matrix is large enough, it is better to pick random cells or do a 2D binary search. In that case you will have to put back the omitted 4 directions into the dirs object.
I understand that this is more of a problem solving than a coding question as such, so my apologies if this post breaks any rules here, but does anyone have any idea how best to go about this?
I am trying to solve the problem, but there is a logical error in my code, or rather I have not considered all the conditions, tell me how to find it.
The problem: An adventurer found himself in a dungeon full of treasures. However, before entering he activated a trap, which in t minutes will flood the entire room.
You are given an array of chests, where chests[i] is the number of treasures in the chest. The explorer can either pick up treasure i, taking one minute, or move to the next chest (i+1), which also takes one minute. He starts at position zero, it is not necessary to reach the end of the array.
Determine the maximum amount of treasure the hero can collect. by writing function getResult(chests,t):Integer
Input:
chests - number of treasures in chests, 2<length(chests)<20, 0<chests[i]<100
t - number of minutes to flood, 0<t<20
Output:
Integer - maximum number of treasures collected
Example 1:
chests = [1, 4, 2] t = 3
getResult(chests, t) = 5 // In the first minute the hero collected treasure from the first chest,
in the second minute, he moved to the next chest, in the third minute, he gets the treasure from it
Example 2:
chests = [7, 8, 9] t = 2
getResult(chests, t) = 8 // In the first minute, the hero goes to the second chest and gets the treasure from it,
than taking the treasure in the first chest
below are my reasons, and code.
the horizontal side of the matrix is moves and captures. They don't differ, because it takes the same amount of time to move or capture an item.
1 unit per move or capture. The chests are arranged vertically, in order of increasing number of moves to the chest, so we can say
If n (number of chests)=4, the values in the chests are in order of distance of moves with the contents of 1, 4, 3, 5
It is possible to take any[j,i] chest in i moves. In 10 moves it is possible to take all items, the point is that the number of moves n for taking
chest is a triangular number, i.e. the sum of the first n natural numbers. The formula for calculating a triangular number is: 1/2 * n * (n+1)
We build a matrix, put the inputs [1, 4, 3, 5] there, and place all the sums of those there, too, as chests.
If one cell of the matrix contains more than 1 chest, we choose maximum.
all combinations without regard to direction, (i.e. 2+3=3+2 without regard to such permutations)
summary matrix: chests and their combinations and steps to get
1__2__3__4__5__6__7__8__9_10
first chest 1, | | | | | | | | |
second chest 0, 4, 5 | | | | | | |
third chest 0, 0, 3, 4, 7, 8, | | | |
fourth chest 0, 0, 0, 5, 6, 9,10, 9 12 13
there are combinations not included in the matrix,
i.e. 4c+1c,2c>4c+3 (throw out the equal in moves option 4+3 chest, this is not the maximum)
So, form a one-dimensional array to select the best (maximal) combinations for each move
maxs_in_onerow=[1,4,5,5,7,9,10,9,12,13]
count sum of elements up to t-1
compare with the element with the number t
ANSWER: sumofchests(0,t-1)>maxs_in_onerow(t) ? return sumofchests(0,t-1) : maxs_in_onerow(t)
// fill in the backpack, output the result
function getResult(chests, t) {
function transpose(a) { //helper func
// Calculate the width and height of the Array
let w = a.length || 0;
let h = a[0] instanceof Array ? a[0].length : 0;
// In case it is a zero matrix, no transpose routine needed.
if(h === 0 || w === 0) { return []; }
let i, j, t = [];
// Loop through every item in the outer array (height)
for(i=0; i<h; i++) {
// Insert a new row (array)
t[i] = [];
// Loop through every item per item in outer array (width)
for(j=0; j<w; j++) {
// Save transposed data.
t[i][j] = a[j][i];
}
}
return t;
}
function sumofsteps(c = chests) {
if (!Array.isArray(c)) c=Array.from({length:c})
return (c.length * (c.length + 1)) / 2;
}
function sumofchests(c = chests) {
return c.reduce((partialSum, a) => partialSum + a, 0);
}
const J = sumofsteps(chests);
const I = (chests.length);
// console.log(`${chests.length}, ${J}, ${I}`);
//create a matrix with the size of the number of chests
//for as many moves as it takes to get all the chests=J
let matrix = Array.from({ length: I }, () => new Array(J).fill(0));
let maxs_in_onerow = [];
// fill with values
let prevstepI = 0;
chests.forEach((element,index) => {
let cchests=chests.slice(0,index)
//the side of the matrix, the moves for moving and taking chests, grows like half a square
for (let i = prevstepI; i <=cchests.length; i++) {
// matrix side, chests,
// nothing before the diagonal, skip
if (i<index) continue
if (i===index) { //diagonal, minimum moves to take
prevstepI=i
matrix[index][i]=element
}
let _x=0
while (_x<i) {
matrix[_x].forEach((el , ind ) => { /* ... */
if (el > 0) {matrix[index][i+ind+1]=element + el}
})
//create combinations of chests
_x+=1
if (_x===i) break
}
}
});
// form maxs_in_onerow=[1,4,5,5,7,9,10,9,12,13]
let jmartix=[]
jmartix=transpose(matrix)
for (let j = 0; j < J; j++) {
let cur=Math.max.apply(null, jmartix[j])
maxs_in_onerow.push(cur);
}
// fill in the backpack, output the result
let res;
if (t === 1) res = chests[0];
if (t >= J) res = sumofchests(chests);
if (t<J) {
let res1=Math.max.apply(null,maxs_in_onerow.slice(0,t))
let res2=sumofchests(maxs_in_onerow.slice(0,t-1))
res = res1>res2 ? res1 : res2
}
// console.log( `${matrix}, ${totalsteps()}, t: ${t}, maxs: ${maxs_in_onerow}, res: ${res} ` );
return res;
}
console.log(` input: [1, 4, 2], 3 \n response: ${getResult([1, 4, 2], 3)}`);
console.log(` input: [7, 8, 9], 2 \n response: ${getResult([7, 8, 9], 2)}`);
My sleep-deprived brain is not up trying to interpret your code or your reasoning. Instead, here's a simple recursive solution:
const maxTreasure = ([c, ...cs], t) =>
t <= 0 || c == undefined
? 0
: c == 0
? maxTreasure (cs, t - 1)
: Math. max (c + maxTreasure ([0, ...cs], t - 1), maxTreasure (cs, t - 1) )
console .log (`maxTreasure ([1, 4, 2], 3) //=> ${maxTreasure ([1, 4, 2], 3)}`);
console .log (`maxTreasure ([7, 8, 9], 2) //=> ${maxTreasure ([7, 8, 9], 2)}`);
We check whether the time has run out or if there are no more chests found, and if so, simply return 0. If the first chest is empty, we have no reasonable alternative than to move on to the next one, so we reduce the time by one and recur with the remaining chests. Otherwise we have to choose the better of two possibilities: taking the current chests' treasures or moving on to the next one. We use Math .max to select one of these, and calculate them by recursion. In one case, we include the current chest (c) and recur with a list of chests that replaces the current chest's value with zero. In the other, we move on to the remaining chests. In either case, we reduce the time by one.
So we have base cases, and three potential recursive calls. In each of those calls, we're reducing the time by 1, so we will eventually reach the case t <= 0.
That same foggy brains isn't going to do the analysis of time complexity here. I wouldn't be surprised if this is horribly inefficient; it's likely of exponential complexity in the number of chests. But it's simple and a good start at thinking of the problem logically. If it turns out too inefficient for real world use (ha!) we can come back at a solution using bottom-up dynamic programming... and that may be what you're attempting.
But I would always start simply, and often recursion is simplest.
Question
How can i compare two adjacent cells thanks to them coordinates?
Documentation which helped me
I already saw these questions, they helped me but they are different from my case:
question on stackOverflow
question on stackOverflow
question on stackOverflow
Mds documentation to build a dynamic table
Code
I've a dynamically generated table
function tableGenerate(Mytable){
for(var i = 0; i < myTable.length; i++) {
var innerArrayLength = myTable[i].length;
for(var j = 0; j<innerArrayLength; j++){
if(myTable[i][j] === 0){
myTable[i][j]="x";
}else{
myTable[i][j]="y";
};
};
$("#aTable").append("<tr><td>"+ myTable[i].join('</td><td>') + "</td></tr>")
}
}
About the interested cells (two global variables) in actualPosition row and cell have random values
var mainTd = {
name: 'interestedValue',
actualPosition:{
row: 5,
cell: 4
}
};
var otherMainTd = {
actualPosition:{
row: 2,
cell: 3
}
};
The final part of the code works in this way:
I save the position of selectedTd in two differents variables
I create the 2d array directions with the coordinates of near cells relatives to the selectedTd
enter in the first if, compare the two cells. If one of the coordinates are the same, you enter in this last if.
function compare(selectedTd) {
let tdRow = selectedTd.actualPosition.row;
let tdCell = selectedTd.actualPosition.cell;
let directions = [
[tdRow - 1, tdCell],
[tdRow + 1, tdCell],
[tdRow, tdCell + 1],
[tdRow, tdCell - 1]
]; //these are the TD near the mainTd, the one i need to compare to the others
let tdToCompare = [];
if (selectedTd.name === 'interestedValue') {
tdToCompare = [otherMainTd.actualPosition.row, otherMainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (directions[i] == tdToCompare) {
console.log('you are here');
}
}
} else {
tdToCompare = [mainTd.actualPosition.row, mainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (directions[i] === tdToCompare) {
console.log('you are here');
}
}
}
};
Now the main problem is: I read the coordinates, I store them in the 2 arrays, I can read them but I cannot able to enter in the if statement.
This is what I want to achieve: compare the coordinates of the blackTd with the coordinates of the red-borders td.
Codepen
the interested functions in the codepen are with different names, but the structure is the same that you saw in this post. I changed the original names because I think it could be more clear with general names instead of the names that i choose.
the interested functions are:
function fight(playerInFight) ---> function compare(selectedTd)
function mapGenerate(map) ---> function tableGenerate(MyTable)
mainTd and otherMainTd ---> character and characterTwo
CodepenHere
Update:
Reading your code again I think I figured out the problem. You're comparing array instances instead of their actual values. See this simple example to illustrate the issue:
var a = [1];
var b = [1];
console.log(a===b);
What you'd need to do in your code is this:
if (selectedTd.name === 'interestedValue') {
tdToCompare = [otherMainTd.actualPosition.row, otherMainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (
directions[i][0] === tdToCompare[0] &&
directions[i][1] === tdToCompare[1]
) {
console.log('you are here');
}
}
} else {
tdToCompare = [mainTd.actualPosition.row, mainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (
directions[i][0] === tdToCompare[0] &&
directions[i][1] === tdToCompare[1]
) {
console.log('you are here');
}
}
}
Now it checks if the values, and thus the cells, are matching.
Recommendations:
If I were you, I would write the method a bit different. Below is how I would do it.
function compare(selectedTd) {
const
// Use destructuring assignemnt to get the row and cell. Since these are
// values that won't be changed in the method declare them as "const". Also
// drop the "td" prefix, it doesn't add anything useful to the name.
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
{ row, cell } = selectedTd.actualPosition,
// Directions can also be a const, it will not be reassigned in the method.
directions = [
[row - 1, cell],
[row + 1, cell],
[row, cell + 1],
[row, cell - 1]
],
// A few things happens in this line:
// - It is a destructuring assignment where the names are changed. In this case
// row and cell are already assigned so it is necessary to give them another name.
// - Don't put the row and cell in an array. You will have to access the actual values
// anyway as you can't compare the array instances.
// - Instead of doing this in the if…else you had, decide here which cell you want to
// look for. It means the rest of the method can be written without wrapping any
// logic in an if…else making it less complex.
{ row: referenceRow, cell: referenceCell } = (selectedTd.name === 'interestedValue')
? otherMainTd.actualPosition
: mainTd.actualPosition,
// Use find instead of a for loop. The find will stop as soon as it finds a match. The
// for loop you had kept evaluating direction items even if the first one was already
// a match.
// The "([row,cell])" is the signature of the callback method for the find. This too is
// a destructuring assignment only this time with one of the arrays of the directions
// array. The first array item will be named "row" and the second item "cell". These
// variable names don't clash with those declared at the top of this method as this
// is a new scope.
// The current directions entry is a match when the row and cell values match.
matchingNeighbor = directions.find(([row, cell]) => row === referenceRow && cell === referenceCell);
// "find" returns undefined when no match was found. So when match is NOT unddefined
// it means directions contained the cell you were looking for.
if (matchingNeighbor !== undefined) {
console.log('you are here');
}
};
const
mainTd = {
name: 'interestedValue',
actualPosition: {
cell: 1,
row: 1
}
},
otherMainTd = {
actualPosition: {
cell: 0,
row: 1
}
};
compare(mainTd);
Orginal answer:
There is quite a bit going on in your question, I hope I understood it properly.
What I've done is create a Grid, you pass this the dimensions and it will create the array for each cell in the grid. Then it returns an object with some methods you can use to interact with the grid. It has the following methods:
cellAtCoordinate: Pass it an X and Y coordinate and it returns the cell.
isSameLocation: Pass it two cells and it checks if the cells are in the same location.
neighborsForCoordinate: Pass it an X and Y coordinate and it returns an array with the cells above, below, to the right, and to the left (if they exist).
With all of this out of the way the compare method becomes a little more manageable. Getting the neighbours is now just a single call, the same for the check if two cells match.
Like I said, I hope this is what you were trying to achieve. If I got the problem wrong and something needs some further explaining, please let me know.
/**
* Creates grid with the provided dimensions. The cell at the top left corner
* is at coordinate (0,0). The method returns an object with the following
* three methods:
* - cellAtCoordinate
* - isSameLocation
* - neighborsForCoordinate
*/
function Grid(width, height) {
if (width === 0 || height === 0) {
throw 'Invalid grid size';
}
const
// Create an array, each item will represent a cell. The cells in the
// array are laid out per row.
cells = Array.from(Array(width * height), (value, index) => ({
x: index % width,
y: Math.floor(index / height)
}));
function cellAtCoordinate(x, y) {
// Make sure we don't consider invalid coordinate
if (x >= width || y >= height || x < 0 || y < 0) {
return null;
}
// To get the cell at the coordinate we need to calculate the Y offset
// by multiplying the Y coordinate with the width, these are the cells
// to "skip" in order to get to the right row.
return cells[(y * width) + x];
}
function isSameLocation(cellA, cellB) {
return (
cellA.x === cellB.x &&
cellA.y === cellB.y
);
}
function neighborsForCoordinate(x, y) {
// Make sure we don't consider invalid coordinate
if (x >= width || y >= height || x < 0 || y < 0) {
return null;
}
const
result = [];
// Check if there is a cell above.
if (y > 0) result.push(cellAtCoordinate(x, y - 1));
// Check if there is a cel to the right
if (x < width) result.push(cellAtCoordinate(x + 1, y));
// Check if there is a cell below.
if (y < height) result.push(cellAtCoordinate(x, y + 1));
// Check if there is a cell to the left.
if (x > 0) result.push(cellAtCoordinate(x - 1, y));
return result;
}
return {
cellAtCoordinate,
isSameLocation,
neighborsForCoordinate
}
}
function compareCells(grid, selectedCell) {
const
// Get the neighbors for the selected cell.
neighbors = grid.neighborsForCoordinate(selectedCell.x, selectedCell.y);
compareAgainst = (selectedCell.name === 'interestedValue')
? otherMainTd
: mainTd;
// In the neighbors, find the cell with the same location as the cell
// we want to find.
const
match = neighbors.find(neighbor => grid.isSameLocation(neighbor, compareAgainst));
// When match is NOT undefined it means the compareAgainst cell is
// a neighbor of the selected cell.
if (match !== undefined) {
console.log(`You are there at (${match.x},${match.y})`);
} else {
console.log('You are not there yet');
}
}
// Create a grid which is 3 by 3.
const
myGrid = Grid(3, 3),
// Place the main TD here:
// - | X | -
// - | - | -
// - | - | -
mainTd = {
name: 'interestedValue',
x: 1,
y: 0
},
// Place the other TD here:
// - | - | -
// Y | - | -
// - | - | -
otherMainTd = {
x: 0,
y: 1
};
// Check if the mainTd is in a cell next to the otherMainTd. It is not
// as the neighboring cells are:
// N | X | N
// Y | N | -
// - | - | -
compareCells(myGrid, mainTd);
// Move the mainTd to the center of the grid
// - | - | -
// Y | X | -
// - | - | -
mainTd.y = 1;
// Compare again, now the main TD is next the the other.
// - | N | -
// YN | X | N
// - | N | -
compareCells(myGrid, mainTd);
I have tried to calculate binary of a number provided in an array using naive for-loop method.
Than I find a solution which works but I am not able to understand how it is working.
I need help to understand this.
const binaryArrayToNumber = arr => {
return arr.reduce((a,b)=>(a<<1|b),0);
};
console.log(binaryArrayToNumber([1,1,1,1]))
Explaining your code:
The << left logical shift) shifts the number to the left. If the number n is 00010110 then:
n << 1 will be 00101100.
n << 2 will be 01011000.
etc
The operator | (betwise or) performs an iclusive logical OR on the bits at the same positions. If the numbers n and m were, respectively 00110100 and 10010101 then:
n = 00110100
m = 10010101
----------------
n | m = 10110101
following these rules: 0 | 0 = 0, 0 | 1 = 1, 1 | 0 = 1 and 1 | 1 = 1.
Now for the reduce part: reduce loop through an array, passing an accumulator (initially set by the user, the last parameter) and the current item of the array to a callback (the function passed as the first parameter) and set the value of the accumulator to the returned value of that callback.
So for each item in the array we shifts the accumulator to the left to make a place for the new bit and then add the bit to that place using the betwise or. Here is an example with explanation:
var arr = [1, 0, 1];
reduce will start with an accumulator equal to 0 (...,0); at end of reduce line) then pass it along with the current item (the first item) to the callback. The the callback will shift the accumulataror (a) to the left by 1:
First shift the accumulator:
a = 00000000;
a << 1 = 00000000;
And then return the betwise or result of the shifted a with the current item from the array b:
b = 00000001;
00000000 | b = 00000001;
Now the new accumulator is the result of the above code (00000001).
reduce then will pass the current accumulator along with the current item from the array (now the second one) to the callback again:
First:
a = 00000001;
a << 1 = 00000010;
and:
b = 00000000;
00000010 | b = 00000010;
reduce will do the same thing for the last item of the array:
First:
a = 00000010;
a << 1 = 00000100;
and:
b = 00000001;
00000100 | 00000001 = 00000101;
Since there is no more items in the array, reduce will return the accumulated value (the accumulator return by the last call to the callback i.e a) as the result.
If the syntax return arr.reduce((a,b)=>(a<<1|b),0); isn't clear for you, it's because you're not familliar with Arrow functions. The exact line could be written using regular functions like this:
return arr.reduce(function(a, b) { // a the accumulator, b the curent item from the array
return (a << 1) | b; // return something to store as the new accumulator
}, 0; // 0 is the initial value of the accumulator
Another way to do it:
without using any binary operation nor reduce:
var arr = [1, 0, 1, 1];
var number = parseInt(arr.join(''), 2);
console.log(number);
arr.join('') will return a string (a concatenation of all the items in the array "1011"). Then parseInt will parse that string as being a binary number.
The reduce function works as an accumulator. The (a,b) pair is actually (total, currentValue). For instance, if you want to calculate sum of 1, 2 and 3, you can use the following code for that:
var sum = [1, 2, 3].reduce(
function(total, currentNumber){ return total + currentNumber; }
, 0);
For each iteration, a value of the total variable is increased for currentNumber and after all elements of the array were iterated, the total is being assigned to the sum variable.
The second parameter of the anonymous function (in this case 0) is the initial value of the sum (before iterating the array elements).
So, the code above is same as this code:
var sum = 0;
for (var i = 1; i <=3; i++)
{
sum = sum + i;
}
Accorting to this answer's comment, I would like to use Unit8Array to achieve the same thing:
var bin = parseInt('1000', 2); //=8
I mean:
inputs are:
length = 4
digits = [1,0,0,0]
and after some TypedArray magic the output will be:
8
Lets see a concrete example with (2 bits length):
counts = {};
var a = [[0,1],[0,0],[0,1],[0,0],[0,0],[1,1]];
for(var i in a){
var id = parseInt(a[i].join(''), 2);
if(!++counts[id])counts[id] = 1;
}
console.log(counts); //{0: 3, 1: 2, 3: 1}
my notes are here.
If you want more speed, handle allready the ids as integers, not array, and have bit changed with & and | operations :
counts = {};
var a = [1,0,1,0,0,3];
for(var i=0; i<a.length; i++ ){
var c = counts[a[i]] || 0 ;
counts[a[i]]= c+1 ;
}
console.log(counts); // { 0: 3, 1: 2, 3: 1 }
to set the k-th bit :
id |= 1 << k;
to clear the k-th bit :
id &= !(1 << k);
to swap the k-th bit :
id ^= (1 << k);
to read the k-th bit :
bitK = (id >> k ) & 1;
So you might write small functions to do those operations, with a 99% chance they will get
inlined by the javascript engine, it will be much faster than array handling + parseInt.
You might even write this code directly. Maybe in some case you will be able to
cache 1 << k, but anyway, shift instructions have a very little cost.
Another advantage is that you can handle several tests in a single instruction,
for instance :
var setRequiredMask = ( 1<<3 | 1<<5 ) ; // to test if bits 3 and 5 set
var clearRequiredMask = ( 1<<2 | 1 <<4 ) ; // to test if bit 2 and 4 cleared
var someRequiredMask = ( 1<<0 | 1 <<6 ) ; // to test if either bit 0 or bit 6 to be set
var satisfyConditions = ((id & setRequiredMask) == setRequiredMask) &&
((id & !clearRequiredMask) ==0 ) &&
(id & someRequiredMask) ;
Here we trade 7 access to memory to quite the same number of boolean operations.
If this test is used in a loop to filter an array, the speed gain is huge.
In the same way you can set / clear / swap several bits in a single instruction, test if one condition set is included in the other, test if they are orthogonal, .... all in a very fast way.
Only limit is that you can handle no more than 32 possibilities with this method.
Then you can store the ids in a typed a typed array, depending on the max number
of possibilities, use only a Int8Array or a Int32Array, but i am not sure the gain
in performance will be that interesting compared to [].
Syntax :
var arr = new Int8Array( _length_ );
or
var arr = new Int32Array( _length_ );
Are you trying to get this parseInt(string, radix)
radix An integer that represents the radix of the above mentioned
string.
Not sure, is like this somewhat you expect
var x = new Uint8Array([1,0,0,0]);
var a = x[0].toString()+x[1].toString()+x[2].toString()+x[3].toString();
console.log(parseInt(a, 2));
console.log(a.length);
Is this useful?
var a = 1 << 3; // shift the bits of 1 (0001) 3 places: 1000
if (a == 8) {
// this block will execute
}
Another example:
var i,
result = 0,
len = 4,
bits = [1, 0, 0, 0];
for (i = 0; i < len; ++i) {
result = (result << 1) + bits[i];
}
alert(result);
Keep in mind, parseInt is probably faster than this.