3

I'm using JS to highlight duplicate values in a table.

What basically the code is doing is adding table rows value in an array and then comparing if it exist and then highlights that row.

The code is working fine but it highlights all duplicate values in the same color(red). What i need it to do is to highlight each group of similar values in different color. Lets say i have 4 group of duplicate values, each group should be highlighted in a different color. Maybe colors need to be generated randomly as there maybe multiple duplicate values in the table.

 $(function() {
    var tableRows = $("#sortable tbody tr"); //find all the rows
    var rowValues = []; //to keep track of which values appear more than once
    tableRows.each(function() { 
        var rowValue = $(this).find(".content").html();
        if (!rowValues[rowValue]) {
            var rowComposite = new Object();
            rowComposite.count = 1;
            rowComposite.row = this;
            rowValues[rowValue] = rowComposite;
        } else {
            var rowComposite = rowValues[rowValue];
            if (rowComposite.count == 1) {
                $(rowComposite.row).css('backgroundColor', 'red');
            }
            $(this).css('backgroundColor', 'red');
            rowComposite.count++;
        }
    });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="sortable">
    <tbody>
        <tr>
            <td class="content">A</td>
        </tr>
        <tr>
            <td class="content">B</td>
        </tr>
        <tr>
            <td class="content">A</td>
        </tr>
         <tr>
            <td class="content">C</td>
        </tr>
         <tr>
            <td class="content">C</td>
        </tr>
    </tbody>
</table>
cнŝdk
  • 31,391
  • 7
  • 56
  • 78
user2334436
  • 949
  • 5
  • 13
  • 34

6 Answers6

2

I'd create an array per content text with the cells that have the same content. Once you have this you can iterate over it and highlight the cells as needed.

To generate the random color I've added a method which has a Set to keep track of the generated colors. It will check if a random generator color has been made before and keeps generating colors until a unique color is generated.

It is possible you wind up with colors that make the text illegible or two random color that don't have enough contrast to tell them apart. So it is not a perfect solution.

function generateRandomInt(max, min = 0) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}

/**
 * This method will return a new method, when the returned method is 
 * called it will return a unique color. Subsequent calls to the color
 * generator will never return the same color.
 */
function colorGenerator() {
  // Create a Set at the function scope which we can use to keep
  // track of the colors generated by the returned method.
  const
    generatedColors = new Set();
    
  return () => {
    let randomColor;
    // Keep generating a random color in the format "rgb(R,G,B)" until 
    // a color is generated that doesn't yet exist in the set. This doesn't
    // take into account that at some point you'll run out of 
    // possible colors (but it will take 16M+ tries).
    do {
      randomColor = `rgb(${generateRandomInt(255)},${generateRandomInt(255)},${generateRandomInt(255)})`;
    } while (generatedColors.has(randomColor));
    
    // Add the generated, unique, color to the set.
    generatedColors.add(randomColor);
    
    // Return the random color.
    return randomColor;  
  };
}

function highlightDoubles(table) {
  const
    // Get all the element with the content CSS class.
    contentCells = table.querySelectorAll('.content'),
    // Create map, the cell content will be the key and the value will be
    // an array with cells that have the key as content.
    contentMap = new Map();
   
  // IE doesn't support forEach on a NodeList, convert it to an array first.
  Array.from(contentCells).forEach(cell => {
    const
      // For each cell check if the content has been encountered before. If so
      // return the map value and else create a new array.
      array = (contentMap.has(cell.textContent))
        ? contentMap.get(cell.textContent)
        : [];
      // Push the current cell into the array.
      array.push(cell)
      // Store the array in the map.
      contentMap.set(cell.textContent, array);
  });

  // Create a color generator, it will create a random
  // color but never the same color twice.
  const 
    randomColor = colorGenerator();
    
  // Iterate over all the entries in the map, each entry is a unique 
  // cell content text
  contentMap.forEach(cells => {
    // When the lengths of the cells array is less than 2 it means 
    // it is not multiple times in the table. Exit now.
    if (cells.length < 2) {
      return;
    }
    
    // Generate a random color for the current content text. This is just
    // a very naive implementation. It doesn't make any promises on readability.
    const
      color = randomColor();
      
    // Apply the random color to all the cells with the same content.
    cells.forEach(cell => {
      cell.style.backgroundColor = color;
    });
  });
}

highlightDoubles(document.getElementById('sortable'));
<table id="sortable">
    <tbody>
        <tr>
            <td class="content">A</td>
        </tr>
        <tr>
            <td class="content">B</td>
        </tr>
        <tr>
            <td class="content">A</td>
        </tr>
         <tr>
            <td class="content">C</td>
        </tr>
         <tr>
            <td class="content">C</td>
        </tr>
    </tbody>
</table>
Thijs
  • 2,341
  • 2
  • 14
  • 22
2

Instead of using an array for the rowValues, you better use an object so you can check over the existence of the key value.

You can also use an array of colors from where you get your dynamic colors and keep shifting the array, whenever you find a new value, so each distinct value would have its relevant distinct color.

And there's no need to check for the count in the else block, because whenever you reach this block it means that this value already exists in the array.

This is how should be your code:

$(function() {
  var tableRows = $("#sortable tbody tr"); //find all the rows
  var colors = ["red", "blue", "green", "yellow", "#f5b"];
  var rowValues = {};
  tableRows.each(function() {
    var rowValue = $(this).find(".content").html();
    if (!rowValues[rowValue]) {
      var rowComposite = new Object();
      rowComposite.count = 1;
      rowComposite.row = this;
      rowValues[rowValue] = rowComposite;
    } else {
      var rowComposite = rowValues[rowValue];
      if (!rowComposite.color) {
        rowComposite.color = colors.shift();
      }
      rowComposite.count++;
      $(this).css('backgroundColor', rowComposite.color);
      $(rowComposite.row).css('backgroundColor', rowComposite.color);
    }
  });
});

Demo:

$(function() {
  var tableRows = $("#sortable tbody tr"); //find all the rows
  var colors = ["red", "blue", "green", "yellow", "#f5b"];
  var rowValues = {};
  tableRows.each(function() {
    var rowValue = $(this).find(".content").html();
    if (!rowValues[rowValue]) {
      var rowComposite = new Object();
      rowComposite.count = 1;
      rowComposite.row = this;
      rowComposite.color = colors.shift();
      rowValues[rowValue] = rowComposite;
    } else {
      var rowComposite = rowValues[rowValue];
      rowComposite.count++;
      $(this).css('backgroundColor', rowComposite.color);
      $(rowComposite.row).css('backgroundColor', rowComposite.color);
    }
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="sortable">
  <tbody>
    <tr>
      <td class="content">A</td>
    </tr>
    <tr>
      <td class="content">B</td>
    </tr>
    <tr>
      <td class="content">A</td>
    </tr>
    <tr>
      <td class="content">C</td>
    </tr>
    <tr>
      <td class="content">C</td>
    </tr>
  </tbody>
</table>
Joe A
  • 119
  • 4
cнŝdk
  • 31,391
  • 7
  • 56
  • 78
0

Whenever you create a new Object in the if statement, do something like

var rowComposite = new Object();
rowComposite.count = 1;
rowComposite.row = this;
var myColor = generateRandomColor();
rowComposite.color = myColor;

And then when you assign color assign it .color instead of just 'red':

$(rowComposite.row).css('backgroundColor', rowComposite.color);

You can read about random colour generation is js here: Random color generator

Viktor1926
  • 48
  • 1
  • 2
  • 11
0

You can generate the random color by using custom function example are as below

$(function() {
    var tableRows = $("#sortable tbody tr"); //find all the rows
    var rowValues = []; //to keep track of which values appear more than once
    tableRows.each(function() { 
        var rowValue = $(this).find(".content").html();
        if (!rowValues[rowValue]) {
            var rowComposite = new Object();
            rowComposite.count = 1;
            rowComposite.row = this;
            rowValues[rowValue] = rowComposite;
        } else {
            var rowComposite = rowValues[rowValue];
            if (rowComposite.count == 1) {
                $(rowComposite.row).css('backgroundColor',getRandomColor());
            }
           // $(this).css('backgroundColor', getRandomColor());
           $(this).css('backgroundColor', 'red');
            rowComposite.count++;
        }
    });
});

function getRandomColor() {
  var letters = '0123456789ABCDEF';
  var color = '#';
  for (var i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="sortable">
    <tbody>
        <tr>
            <td class="content">A</td>
        </tr>
        <tr>
            <td class="content">B</td>
        </tr>
        <tr>
            <td class="content">A</td>
        </tr>
         <tr>
            <td class="content">C</td>
        </tr>
         <tr>
            <td class="content">C</td>
        </tr>
        <tr>
            <td class="content">C</td>
        </tr>
    </tbody>
</table>
SantoshK
  • 1,789
  • 16
  • 24
0
function random_rgba() {
    var o = Math.round, r = Math.random, s = 255;
    return 'rgba(' + o(r()*s) + ',' + o(r()*s) + ',' + o(r()*s) + ',' + r().toFixed(1) + ')';
}

$(function() {

    var tableRows = $("#sortable tbody tr"); //find all the rows
    var rowValues =  {}; //to keep track of which values appear more than once
    var color = "";
    tableRows.each(function() { 
        var rowValue = $(this).find(".content").html();
        if (rowValues[rowValue]){
            color = rowValues[rowValue];
        }else{
            color = random_rgba();
          rowValues[rowValue] = color;
        }
        $(this).css('backgroundColor', color);
    });
});

here you have it working: https://jsfiddle.net/zn0g9u34/9/

Curlas
  • 879
  • 5
  • 14
0

You can try this. Simple and stylish:

$(function(){
    var st=document.createElement("style");
    document.head.appendChild(st);
    var sty={}, s;
    var A={}, cou=1;
    $("#sortable .content").each(function(i, c){
         if(!sty[c.innerHTML]){
            sty[c.innerHTML]=1;
            A[c.innerHTML]=cou++;
         }else sty[c.innerHTML]+=1;
         c.setAttribute("data-w", A[c.innerHTML]);
    });
    for(s in sty){
        if(sty[s]>1){
            st.sheet.insertRule("#sortable .content[data-w='"+A[s]+"']{background-color:rgb("+
                parseInt(Math.random()*256)+","+ 
                parseInt(Math.random()*256)+","+
                parseInt(Math.random()*256)+
            ");}", 0);
        };
    };
});

Excuse me, I am typing with my mobile phone and I have not access to fiddle.


Try it online!

Hassan Sadeghi
  • 1,316
  • 2
  • 8
  • 14