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<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 < minDistance) {
minDistance = distance;
nearestHex = currentColor.hex;
}
};
return nearestHex;
}();
closestHexFromRgb(hexToRgb(hex));
};
1 Answer 1
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.