Given hexadecimal rgb color, find the closest predefined color The following is a function that accepts a hex value (with or without a #, 3 or 6 character) and returns another hex value that is "closest" to it from a set of static hex values defined within the function. It involves a conversion to RGB.

How do I make this function more "elegant"? This is more of a question in general JS syntax and style, but I figured this was a good general example to present. I'd imagine I need to include some kind of nested functions, anonymous functions or closures that handle each part of the algorithm.

Here's the code, taking colors from Google's logo, for instance:

function nearestHex(hex) {

  var hexToRgb = function(hex) {
    var shortRegEx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
    hex = hex.replace(shortRegEx, function(full, r, g, b) {
      return [r, r, g, g, b, b].join();
    });
    var longRegEx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?/i;
    var rgbArray  = longRegEx.exec(hex);
    var rgbObj = rgbArray ? {
      r: parseInt(rgbArray[1], 16),
      g: parseInt(rgbArray[2], 16),
      b: parseInt(rgbArray[3], 16)
    } : null;
    return rgbObj;
  }();

  var closestHexFromRgb = function(rgbObj) {
    if (!rgbObj) {
      throw new Error("The hex you provided is not formatted correctly. Please try in a format such as '#FFF' or '#DDFFDD'.");
    };

    var staticColors = [
      { color: 'red',    hex: '#EA4235', rgb: { r: 234, g: 66,  b: 53  } },
      { color: 'green',  hex: '#34A853', rgb: { r: 512  g: 168, b: 83  } },
      { color: 'blue',   hex: '#4285F4', rgb: { r: 66,  g: 133, b: 244 } },
      { color: 'yellow', hex: '#FBBC05', rgb: { r: 251, g: 188, b: 5   } }
    ];
    var minDistance = Number.MAX_SAFE_INTEGER;
    var nearestHex = null;

    for (var i=0; i&lt;staticColors.length; i++) {
      var currentColor = staticColors[i];
      var distance = Math.sqrt(
        Math.pow((rgbObj.r - currentColor.rgb.r), 2) +
        Math.pow((rgbObj.g - currentColor.rgb.g), 2) +
        Math.pow((rgbObj.b - currentColor.rgb.b), 2)
      );
      if (distance &lt; minDistance) {
        minDistance = distance;
        nearestHex = currentColor.hex;
      }
    };
    return nearestHex;
  }();

  closestHexFromRgb(hexToRgb(hex));

};
Fixing and improving the short regex handling

This won't work as intended:


hex = hex.replace(shortRegEx, function(full, r, g, b) {
  return [r, r, g, g, b, b].join();
});



The default separator of join is a comma, so this will join the hexadecimal digits by commas, when you want empty string: .join("")

In any case, instead of the anonymous function + array + joining,
it will be simpler to use the capture groups of the regex in the replacement string:

hex = hex.replace(shortRegEx, "$1$1$2$2$3$3");


Don't repeat yourself

Instead of repeating parseInt(x, 16) multiple times here:


  r: parseInt(rgbArray[1], 16),
  g: parseInt(rgbArray[2], 16),
  b: parseInt(rgbArray[3], 16)



It would be better to create a function for it.

The same goes for this snippet in the distance calculation:


    Math.pow((rgbObj.r - currentColor.rgb.r), 2) +
    Math.pow((rgbObj.g - currentColor.rgb.g), 2) +
    Math.pow((rgbObj.b - currentColor.rgb.b), 2)





When defining the static colors here,
it's not obvious that the rgb values are correct:


  { color: 'red',    hex: '#EA4235', rgb: { r: 234, g: 66,  b: 53  } },
  { color: 'green',  hex: '#34A853', rgb: { r: 512  g: 168, b: 83  } },
  { color: 'blue',   hex: '#4285F4', rgb: { r: 66,  g: 133, b: 244 } },
  { color: 'yellow', hex: '#FBBC05', rgb: { r: 251, g: 188, b: 5   } }



You could make it obvious by reusing the hexToRgb function to create the rgb values.