How to compare cells in JavaScript table to each other and test for equality? How does indexOf work?

I created a table in my HTML code. It has 9 columns and 13 rows. It gets filled up completely by a JavaScript loop that fills it with names of people from a few arrays. However, I want to add a validation step that makes sure that no two cells within a row hold the same value and that the value of a each cell does not repeat in the cell directly beneath it.

Since I am only able to access the values of the cells of the table as a NodeList, I decided to make it into an array to use the IndexOf property to search through the array:

var table1 = document.getElementsByTagName("td");
var table1Array = [];    //number of "td" elements on page
for (i = 0; i < 117; i++) {
    table1Array[i] = table1[i].innerHTML;
}

I don't know of a more elegant method (sort of a beginner here). So I set up some nested loops to compare each cell to each element in the row array I create (for all 13 rows):

function validateCells() {
for (i = 0; i < 117; i = i + 9) { //increment for going to next column (after 9 cells or elements in array)
    for (j = 0; j < 8; j++) {
        var curCell = table1Array[i];
        var curRow = [];         //I'm ignoring the first column which is "date", so it has 8 elements rather than 9
        for (k = 0; k < 8; k++) {
            curRow[k] = document.getElementById("row" + j).children[k].innerHTML;
        }
        if (curRow.indexOf(curCell) != -1) {
            curCell = "CONFLICT"; //trying to change value in table. Doesn't work.
        }
    }
}
}

Unfortunately, this won't work. I don't even know if modifying the value of the curCell reference variable will actually change the value of the table1Array at that location, even less if it will change the data in the table.

Am I using the indexOf property correctly? Do I use the != comparison operator or !==? Does indexOf return the index as a Is there any other less complicated, more elegant to do this? I just started with jQuery, maybe it can help simplify the code and make it less error-prone.

Sorry for the all the questions, I'm really trying to understand how all of this works. Thanks!

Answers:

Answer

You can use the table rows and cells collections for the iteration. The following does a literal comparison of the text content, you may wish to process the text first to "normalise" it in regard to whitespace.

<table id="t0">
 <tr><td>foo<td>bar<td>fum</td>
 <tr><td>fum<td>bar<td>foo</td>
 <tr><td>foo<td>fum<td>fum</td>
</table>

<script>

compareRows(document.getElementById('t0'));

function compareRows(table) {
  var row, rows = table.rows;
  var cell, cells;
  var rowText;

  // For each row in the table
  for (var i=0, iLen=rows.length; i<iLen; i++) {
    row = rows[i];
    cells = row.cells;

    // Compare the text in each cell
    for (var j=0, jLen=cells.length; j<jLen; j++) {
      cell = cells[j];

      for (var k=0; k<jLen; k++)

        if (k != j && cells[k].textContent == cell.textContent) {
          // cell text is repeated in current row
          console.log('row ' + i + ' cell ' + j + ' text repeated in cell ' + k);
      }

      // Compare with the text in the cell immediately below (if there is one)
      if (i < iLen-2 && cell.textContent == rows[i+1].cells[j].textContent) {
        // cell text is repeated in next row
        console.log('row ' + i + ' cell ' + j + ' text repeated in row ' + (i+1));
      }
    }
  }
}
</script>

Note that repeated text in a row will be reported twice.

The above uses the textContent property, which may be supported as innerText in some user agents. It also runs about 10 times faster than the jQuery alternative.

Answer

You should get an array of rows, each row is an array of cells. That way the validation is much easier. I'm not sure about how you want to show the conflict. In this demo I've just highlight the duplicated cells (conflicted) in red (at least I like this kind of showing conflict rather than modifying the conflicted cells' text).

HTML:

<table>
  <tr><td>1</td><td>2</td><td>3</td></tr>
  <tr><td>1</td><td>5</td><td>6</td></tr>
  <tr><td>7</td><td>8</td><td>7</td></tr>
  <tr><td>8</td><td>9</td><td>10</td></tr>
</table>
<button>Check constraints</button>

CSS:

td {
  width:100px;
  height:50px;
  border:1px solid black;
}
table {
  border:1px solid black;
  border-collapse:collapse;
}
td.invalid {
  background:red;
}

JS:

$('td').attr('contenteditable',true);
var cell;     
function highlight(){    
  $(arguments).toggleClass('invalid',true);
}
function checkConstraints(e){
  //reset style before re-checking
  $('td.invalid').toggleClass('invalid');
  //get rows as an array of array
  var rows = $('tr').map(function(elem,i){
      return [$(this).children('td').toArray()];
  }).toArray();        
  //loop through the rows
  for(var i = 0; i < rows.length; i++){
    cell = {};        
    for(var j = 0; j < rows[i].length; j++){
        var cellText = $(rows[i][j]).text();
        if(cell[cellText]) {  
            highlight(cell[cellText], rows[i][j]);                
        } else {
            cell[cellText] = rows[i][j];
        }
        if(i < rows.length - 1 && 
           cellText == $(rows[i+1][j]).text()){
           highlight(rows[i][j],rows[i+1][j]);
        }            
    }        
  }
}
$('button').click(checkConstraints);

Demo.

Note that, I set contenteditable for all the cells (td), you can edit the cells text to what you want and click the button to test the demo.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.