1

I have already written a working method, that can convert an array of rgb values to a hexadecimal color string:

/**
 * @param {Number[]} arr Array with three entries (rgb: [0-255, 0-255, 0-255]).
 * @return {String} Hex color as a string (rgb: '#000000' - '#ffffff').
 */
function arrToHex(arr) {
  return '#' + arr
    .map(v => ('0' + v.toString(16)).slice(-2))
    .join('');
}

But I'm unsure whether this method is the best approach when it comes to efficiency. This method is called pretty often in my code.

Especially the part with ('0' + v.toString(16)).slice(-2) (to get a double hexadecimal) seems too complicated to me. Can this be done better? Is this functional approach even the best?


Performance evaluations:

Results show average seconds calculated by console.time if run with Google Chrome v83 (64bit, Linux). Ordered from best to worst.

Iterative and lookup table solution by selbie: (~1.1sec)

const hextable = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
function arrToHex(arr) {
  let s = '#';
  for (let i = 0; i < arr.length; i++) {
    s += hextable[(arr[i] / 16) | 0];
    s += hextable[arr[i] % 16 | 0];
  }
  return s;
}

console.time('arrToHex');
for (var r = 0; r < 256; r++)
for (var g = 0; g < 256; g++)
for (var b = 0; b < 256; b++) {
  arrToHex([r, g, b]);
}
console.timeEnd('arrToHex');

Iterative and no lookup table solution by selbie: (~1.8sec)

function arrToHex(arr) {
  let s = '#';
  for (let i = 0; i < arr.length; i++) {
    s += ((arr[i] / 16) | 0).toString(16);
    s += ((arr[i] % 16) | 0).toString(16);
  }
  return s;
}
console.time('arrToHex');
for (var r = 0; r < 256; r++)
for (var g = 0; g < 256; g++)
for (var b = 0; b < 256; b++) {
  arrToHex([r, g, b]);
}
console.timeEnd('arrToHex');

Non-iterative solution by [deleted]: (~2.6sec)

function arrToHex(arr) {
  const rgb = arr[2] | (arr[1] << 8) | (arr[0] << 16);
  return '#' + rgb.toString(16).padStart(6, '0');
}

console.time('arrToHex');
for (var r = 0; r < 256; r++)
for (var g = 0; g < 256; g++)
for (var b = 0; b < 256; b++) {
  arrToHex([r, g, b]);
}
console.timeEnd('arrToHex');

My bad functional approach: (~6.5sec)

function arrToHex(arr) {
  return '#' + arr
    .map(v => ('0' + v.toString(16)).slice(-2))
    .join('');
}

console.time('arrToHex');
for (var r = 0; r < 256; r++)
for (var g = 0; g < 256; g++)
for (var b = 0; b < 256; b++) {
  arrToHex([r, g, b]);
}
console.timeEnd('arrToHex');

3
  • 1
    .toString(16).padStart(2,'0') is another way around to make sure you have 2 hex-digits, other than that, your code looks pretty viable Commented Jun 9, 2020 at 8:05
  • Given that browsers can do the conversion all by themselves, since each of the following are valid colours in css: #rgb or #rrggbb or #rrggbbaa or rgb(r,g,b) or rgba(r,g,b,a), and that the conversion will be done by native code, rather than javascript, is there any real reason to do the conversion at all? Without knowing more, I wouldn't discount simplifying this whole method to the roughly equivalent: reurn( 'rgb(' +r+ ',' +g+ ',' +b+ ')' ); Commented Jun 9, 2020 at 8:37
  • @enhzflep I already know the rgb function in CSS, but this isn't helpful in my use case. Commented Jun 9, 2020 at 9:25

1 Answer 1

1

You could try benchmarking this code:

const hextable = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];

function arrToHex(arr) {
    let s = '#';
    for (let i = 0; i < arr.length; i++) {
        s += hextable[(arr[i] / 16) | 0];
        s += hextable[arr[i] % 16 | 0];
    }
    return s;
}

Or as discussed in the comments, to avoid the table lookup and rely on the runtime to do the mapping:

function arrToHex(arr) {
    let s = '#';
    for (let i = 0; i < arr.length; i++) {
        s += ((arr[i] / 16) | 0).toString(16);
        s += ((arr[i] % 16) | 0).toString(16);
    }
    return s;
}
Sign up to request clarification or add additional context in comments.

6 Comments

Neat, but the Math.floor functions can be reduced: Math.floor(arr[i] / 16) to arr[i] / 16 | 0 and Math.floor(arr[i] % 16) to just arr[i] % 16
That's a neat idea. I've made it arr[i]%16|0 so it can reasonably support decimal numbers as well. It doesn't handle negative or out of range values perfectly. That's an exercise for the OP (you) to consider.
Why not hextable = [...'0123456789abcdef'] and hextable[0|arr[i]/16]? (the second may somewhat increase the performance, by the way)
The JS doc defines that is allowed to be given to the function, everything else doesn't bother me.
|@selbie @YevgenGorbunkov I'll do a performance analysis and edit my answer to provide the results. Give me a moment...
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.