A function that sorts between equal number of a value in true or false

Viewed 99

I am currently struggling with making a function that checks for numbers in a 4x4 table, in right and wrong systematically.

I have the function: game([ ["1","2","1","2"], ["2","1","1","2"], ["2","2","1","1"], ["1","1","2","2"]])

What i am trying to do, is to make sure, that it checks if there is an equal number of 1's and 2's in each row and column and loads the message "true" if there is, and "false" if there is not

4 Answers

I'll do the following. First declare 2 object like this:

var rows= {1:[], 2: []};
var columns= {1:[], 2: []};
// it will count how many of each number you have in rows and in columns

Then run over the arrays to count each value:

data.forEach((arr, idx) => {
  rows[1][idx] = 0;
  rows[2][idx] = 0;
  arr.forEach((value, _idx) =>{
   if(!columns[value][_idx]){
    columns[value][_idx] = 0;
    }
    columns[value][_idx] += 1;
    rows[value][idx] +=1;
  });
})

Then you will have to compare values from rows[1] and rows[2] for the rows and columns[1] and columns[2] for the columns

rows[1].forEach((val, idx) => console.log(val === rows[2][idx]));
columns[1].forEach((val, idx) => console.log(val === columns[2][idx]));

A Nested loop goes through the array sequentially one row after the other. However, if you change the order of the indices (i and j), the traversal will be column-wise. Hence in one nested loop, you can traverse row-wise and column-wise.

function game(arr) {

    let oneRow = 0; //calculates ones in a row
    let oneCol = 0; //calculates ones in a col
    let twoRow = 0; //calculates twos in a row
    let twoCol = 0; //calculates twos in a col
    
    let dim = arr.length;

    for (i = 0; i < dim; i++) {
        for (j = 0; j < dim; j++) {
            if (arr[i][j] == '1') oneRow++;
            else twoRow++;
            if (arr[j][i] == '1') oneCol++;
            else twoCol++;
        }

        if (oneRow === twoRow && oneCol === twoCol) {
            oneRow = oneCol = twoRow = twoCol = 0;
            continue;
        } else {
            return false;
        }
    }

    return true
}

console.log(
  game([["1", "2", "1", "2"],
        ["2", "1", "1", "2"],
        ["2", "2", "1", "1"],
        ["1", "1", "2", "2"],
       ])
);

First, we will create a transpose of our given matrix, now we can traverse and check the numbers of 1's and 2's from both rows and columns very easily.

Next, we just filter the rows and columns based on 1's and 2's and see if the number of 1 and 2 are the same, if not, then immediately return false and if our game array satisfy our condition of equal 1's and 2's, return true

function game(gameArray) {
  let transpose = [];

  for (let i = 0; i < gameArray.length; i++) {
    let temp = [];
    for (let j = 0; j < gameArray.length; j++) {
      temp.push(gameArray[j][i]);
    }
    transpose.push(temp);
  }

  console.table(gameArray);
  console.table(transpose);

  for (let i = 0; i < gameArray.length; i++) {
    aArrOne = gameArray[i].filter((elem) => elem === "1").length;
    aArrTwo = gameArray[i].filter((elem) => elem === "2").length;
    tArrOne = transpose[i].filter((elem) => elem === "1").length;
    tArrTwo = transpose[i].filter((elem) => elem === "2").length;

    if (aArrOne !== aArrTwo || tArrOne !== tArrTwo) {
      return false;
    }
  }

  return true;
}

console.log(
  game([
    ["1", "2", "1", "2"],
    ["2", "1", "1", "2"],
    ["2", "2", "1", "1"],
    ["1", "1", "2", "2"],
  ])
);

While the original problem describes a square table (equal number of columns and rows) and only two possible values, I thought it'd be interesting to solve this for square and non-square tables, and an unlimited number of unique values.

This solution "fails fast": as soon as we find a single row or column with unequal values, we return "false" and stop iterating over rows/columns. If we find an unequal-value row, we don't even need to do the extra computational work of pivoting the table and checking columns.

/*

At each index of the row, check if there's a corresponding key ("column") in the map.

If there is, push this value into that "column".
If there's not, create the "column" for this row index and set its first value to this value.

Finally, convert the Map back to the original nested-Array structure
with some clever shorthand: brackets around the spread operator.

*/

function pivot(table) {
  console.log('pivoting table!')
  const rotated = new Map()
  for (row of table) {
    row.forEach((v, i) => rotated.get(i) ? rotated.get(i).push(v) : rotated.set(i, [v]))
  }
  return [...rotated.values()]
}


/*

For each element in the array, check to see if we've "seen" the value yet.

If yes, increase the count in the map for this value
If no, create a new key in the map for this value, and start counting.

After collecting counts, make a Set of the counts, which will keep only unique values.

If all the element counts are equal, this Set's size will be 1.
If all the element counts weren't equal, the Set will be larger than 1.

*/

function areElementCountsUnequal(arr) {
  const counts = new Map()
  arr.forEach(v => counts.get(v) ? counts.set(v, counts.get(v) + 1) : counts.set(v, 1))
  const uniqueElementCounts = new Set([...counts.values()])
  return uniqueElementCounts.size !== 1
}


function game(table) {
  for (row of table) {
    if (areElementCountsUnequal(row)) {
      console.log(`row unequal: ${row}`)
      return 'false'
    }
  }

  const tablePivoted = pivot(table)
  for (col of tablePivoted) {
    if (areElementCountsUnequal(col)) {
      console.log(`col unequal: ${col}`)
      return 'false'
    }
  }
     
  // we must not have found any unequal ones!
  return 'true'
}


const inputTable1 = [
  ["1","2","1","2"],
  ["2","1","1","2"],
  ["2","2","1","1"],
  ["1","1","2","2"]
]

const result1 = game(inputTable1)
console.log(result1)

console.log('---- next game ----')

const inputTable2 = [
  ["1","3","1","2","3","2"],
  ["2","1","1","2","3","2"],
  ["2","2","1","1","1","2"],
  ["1","1","2","2","1","2"]
]

const result2 = game(inputTable2)
console.log(result2)

Related