1

Note on title: I really did not know how to perfectly name this question. If someone can suggest a more appropriate title, please do.

I have a table where headers must be colored based on the first 7 characters of their name.

First 7 characters are yyyy.mm. For example 2017.10.

Here is my need:

  • I want every occurrence of same 7 chars to be the same color (cell's background color). In other words, if cell's first 7 chars are 2017.10 - they all should be the same color. If it is 2017.09, they all should be the same color BUT different from 2017.10 or any other date.

I am trying to do it but having really hard time finishing it. I feel like I am close...

JS:

function setHeaderColor() {
  const mainTable = document.getElementById('main-table');
  const headerRow = document.querySelectorAll('#main-table tr:first-child th');
  const test = []; // Holds first 7 chars and background color of each column header
  const colors = [
    '#FF6633', '#FFB399', '#FF33FF', '#FFFF99', '#00B3E6',
    '#E6B333', '#3366E6', '#999966', '#99FF99', '#B34D4D',
    '#80B300', '#809900', '#E6B3B3', '#6680B3', '#66991A',
    '#FF99E6', '#CCFF1A', '#FF1A66', '#E6331A', '#33FFCC',
    '#66994D', '#B366CC', '#4D8000', '#B33300', '#CC80CC',
    '#66664D', '#991AFF', '#E666FF', '#4DB3FF', '#1AB399',
    '#E666B3', '#33991A', '#CC9999', '#B3B31A', '#00E680',
    '#4D8066', '#809980', '#E6FF80', '#1AFF33', '#999933',
    '#FF3380', '#CCCC00', '#66E64D', '#4D80CC', '#9900B3',
    '#E64D66', '#4DB380', '#FF4D4D', '#99E6E6', '#6666FF'
  ];

  headerRow[1].style.backgroundColor = colors[1];

  // Extract first 7 characters from column header name
  for (let i = 0; i < headerRow.length; i++) {
    test.push({
      version: headerRow[i].innerHTML.substring(0, 7),
      color: headerRow[i].style.backgroundColor || null
    });
  }

  for (let i = 1; i < test.length; i++) {
    if (test[i].version === test[i - 1].version) {
      test[i].color = test[i - 1].color
    } else {
      test[i].color = colors[Math.floor(Math.random() * colors.length)];
    }
  }

  for (let i = 0; i < headerRow.length; i++) {
    headerRow[i].style.backgroundColor = test[i].color;
  }
}

document.addEventListener('DOMContentLoaded', setHeaderColor);
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">

<table class="w3-table-all" id="main-table">
  <tr>
    <th class="w3-center"> Name</th>
    <th class="w3-center">2017.10-T-2018_08_30 ms_201709.</th>
    <th class="w3-center">2017.09-T-2018_08_30 ms_201709.</th>
    <th class="w3-center">2017.10-T-2018_08_30 ms_201709</th>
    <th class="w3-center">2017.09-T-2018_08_30 ms_201709</th>
    <th class="w3-center">2017.08-T-2018_08_30 ms_201709</th>
  </tr>
</table>

As you can see, every time colors are different. What I am doing now is comparing cell to the previous cell ([i - 1]). I know I should be checking whether this value exists in an array test and then getting its associated color, but I cannot come up with a way to do that. I tried indexOf, includes and no success...

tera_789
  • 489
  • 4
  • 20
  • 2
    You seem to be asking 2 separate questions here 1) How to set the colors correctly 2) how to persist the colors between sessions. I don't see any attempt at #2. What have you tried. Also, you say *I want every occurrence of same 7 chars to be the same color.* Foreground or back? If one cell contains two values, it can only have one background color. Please clarify. – Scott Marcus Nov 06 '18 at 22:04
  • 1
    If you want the colors to be saved, than you need to use localStorage – epascarello Nov 06 '18 at 22:05
  • @ScottMarcus Ok, I deleted 2nd question completely. I need to color cell's background - exactly how I am doing it right now. – tera_789 Nov 06 '18 at 22:07
  • Possible duplicate of [How do I check if an array includes an object in JavaScript?](https://stackoverflow.com/questions/237104/how-do-i-check-if-an-array-includes-an-object-in-javascript) – Heretic Monkey Nov 06 '18 at 22:16
  • @HereticMonkey No it is not. I already mentioned that I tried using `includes` – tera_789 Nov 06 '18 at 22:17
  • There are 43 answers on that question, [including one that makes no mention of `includes` or `indexOf`](https://stackoverflow.com/a/1953622/215552)... – Heretic Monkey Nov 06 '18 at 22:20

3 Answers3

1

So just create a lookup table, if the key exists return it, if it does not grab a new color.

const colors = ['#FF6633', '#FFB399', '#FF33FF', '#FFFF99', '#00B3E6']
const used = {}
function getColor (key) {
  used[key] = used[key] || colors.shift()
  return used[key]
}

console.log("aaa", getColor("aaa"))
console.log("bbb", getColor("bbb"))
console.log("aaa", getColor("aaa"))
console.log("bbb", getColor("bbb"))

How to store it, use localStorage

// default color list
const colors = ['#FF6633', '#FFB399', '#FF33FF', '#FFFF99', '#00B3E6']
// read local storage, if empty, than use empty object
const used = JSON.parse(localStorage.getItem("used") || "{}")
// remove any colors used already so we do not reuse them
Object.values(used).forEach(cc => {
  // find it in the array
  const ind = colors.indexOf(cc)
  // if found, remove it
  if (ind > -1 ) {
    colors.splice(ind, 1)
  }
})

function getColor (key) {
  // check if we have it, if not grab a new one
  used[key] = used[key] || colors.shift()
  // return the code
  return used[key]
}

console.log("aaa", getColor("aaa"))
console.log("bbb", getColor("bbb"))
console.log("aaa", getColor("aaa"))
console.log("bbb", getColor("bbb"))
console.log("bbb", getColor("ccc"))

// set the localstorage with the used values
localStorage.setItem("used", JSON.stringify(used))
epascarello
  • 204,599
  • 20
  • 195
  • 236
  • I don't quite get the `key`... What do you pass as the `key`? – tera_789 Nov 06 '18 at 22:28
  • Because that is what you wanted! "2017.09" is one..... "2017.10" is another.... etc – epascarello Nov 06 '18 at 22:34
  • What I am asking is what should I pass as the key in my case. In other words, can you give me an example that relates to MY code. I can't understand how to combine what you answered with what I have. Thanks – tera_789 Nov 06 '18 at 22:36
  • Worked like a charm:) Thanks a lot. I did not even use storage part, and the colors are always the same. I close window, come back, and colors are same. What is the difference than between leaving it as it is or using your approach of storing them on local storage? – tera_789 Nov 06 '18 at 22:59
  • well do the dates get removed over time? Than the colors will eventually change..... If you do it randomly like you did before than it would need to be stored. – epascarello Nov 07 '18 at 03:22
  • No, the dates do not get removed. They only get added. I think I can just leave it as it is - without local storage – tera_789 Nov 07 '18 at 03:48
0

It sounds like you want to look up whether you've previously assigned a color value to a yyyy.mm string. The best data structure for this sort of lookup is a map, not an array.

In Javascript it's pretty common to use objects as hash maps:

const colorMap = {};
const colors = ['#FF6633', '#FFB399', '#FF33FF']

// use this function by passing in your "yyyy.mm" strings 
function getColorFor(firstSevenChars) {
    if(colorMap[firstSevenChars] !== undefined) {
         // we've encountered this character combo before! 
         // retrieve the corresponding color.
         return colorMap[firstSevenChars];
    }
   
    // get the next free color, which is the n+1th color, 
    // n being the number of colours already assigned.
    // use modulo (%) to ensure that we don't exceed the colors array.
    let newColor = colors[ Object.keys(colorMap).length % colors.length ];
     
    // associate the yyyy.mm code with the new color and return it.
    colorMap[firstSevenChars] = newColor;
    return newColor;
}

console.log("2018.01", getColorFor("2018.01"));
console.log("2018.02", getColorFor("2018.02"));
console.log("2018.03", getColorFor("2018.03"));
console.log("out of colors! loop through them again...");
console.log("2018.04", getColorFor("2018.04"));
console.log("2018.05", getColorFor("2018.05"));
Daniel Rothig
  • 696
  • 3
  • 10
  • Problem is that 2018.01 and 2018.04 have the same color. I want them to be different. But if there was another 2018.01 - it should have #FF6633 – tera_789 Nov 06 '18 at 22:31
  • So long as you provide more colors than you have yyyy.mm values, you'll have a new color every time! The modulo is just prevents your code from erroring if you have a large number of different yyyy.mm values – Daniel Rothig Nov 06 '18 at 22:33
0

You need to use an inner for loop which will find all the matching versions and set the colour.

change the loop to the following.

for (let i = 1; i < test.length; i++) {
    for(let j=i;j<test.length; j++){
        if (test[i].version === test[j].version) {
          test[i].color = test[j].color
        } else {
          test[i].color = colors[Math.floor(Math.random() * colors.length)];
        }
    }
}
Arjun
  • 56
  • 2