# Find the closest three-digit hex colour

In CSS, colours can be specified by a "hex triplet" - a three byte (six digit) hexadecimal number where each byte represents the red, green, or blue components of the colour. For instance, #FF0000 is completely red, and is equivalent to rgb(255, 0, 0).

Colours can also be represented by the shorthand notation which uses three hexadecimal digits. The shorthand expands to the six digit form by duplicating each digit. For instance, #ABC becomes #AABBCC.

Since there are fewer digits in the hex shorthand, fewer colours can be represented.

## The challenge

Write a program or function that takes a six digit hexadecimal colour code and outputs the closest three-digit colour code.

Here's an example:

• Input hex code: #28a086
• Red component
• 0x28 = 40 (decimal)
• 0x22 = 34
• 0x33 = 51
• 0x22 is closer, so the first digit of the shortened colour code is 2
• Green component
• 0xa0 = 160
• 0x99 = 153
• 0xaa = 170
• 0x99 is closer, so the second digit is 9
• Blue component
• 0x86 = 134
• 0x77 = 119
• 0x88 = 136
• 0x88 is closer, so the third digit is 8
• The shortened colour code is #298 (which expands to #229988)

Your program or function must accept as input a six digit hexadecimal colour code prepended with # and output a three digit colour code prepended with #.

## Examples

• #FF0000 → #F00
• #00FF00 → #0F0
• #D913C4 → #D1C
• #C0DD39 → #BD3
• #28A086 → #298
• #C0CF6F → #BC7

## Scoring

This is a code-golf challenge, so shortest answer in your language wins! Standard rules apply.

• "adding together the difference between each component of the full colour code and the corresponding component of the shorthand colour code" - this part is confusing. There's no adding anywhere, right? Jun 28, 2019 at 20:21
• Note that if you simply drop alternate digits then each short colour represents an equal number of full colours, so that could be considered to make a better representation than nearest colour.
– Neil
Jun 28, 2019 at 20:24
• Saw this in the Sandbox but forgot to mention that I don't think requiring the # adds anything to the challenge. Jun 28, 2019 at 20:24
• May we output in lowercase? Jun 28, 2019 at 21:05
• 0x22 is 34, not 30 Jul 1, 2019 at 13:13

# 05AB1E, 13 bytes

ćs2ôH8+17÷hJ«


Try it online!

### How?

ćs2ôH8+17÷hJ« | string, S   e.g. stack: "#B23F08"
ć             | decapitate              "B23F08", "#"
s            | swap                    "#", "B23F08"
2           | two                     "#", "B23F08", 2
ô          | chuncks                 "#", ["B2", "3F", "08"]
H         | from hexadecimal        "#", [178, 63, 8]
8        | eight                   "#", [178, 63, 8], 8
+       | add                     "#", [186, 71, 16]
17     | seventeen               "#", [186, 71, 16], 17
÷    | integer divide          "#", [10, 4, 0]
h   | to hexadecimal          "#", ["A", "4", "0"]
J  | join                    "#", "A40"
« | concatenate             "#A40"
| print top of stack

• I thought about doing N 05AB1E answer too - unless I’ve missed something, hexadecimal conversion in Jelly takes quite a lot of bytes! Jun 29, 2019 at 6:39
• Yeah, no built-in for any textual base conversions in Jelly. Jun 29, 2019 at 12:53
• "ć decapitate" That's another way to describe it, lol. :D Nice answer though, +1 from me. Jul 1, 2019 at 7:13

# Japt, 16 bytes

r"%w"²_n16_r17Ãg

r"%w"²_n16_r17Ãg     :Implicit input of string
r                    :Replace
"%w"                :RegEx /\w/g
²               :Duplicate, giving /\w\w/g
_              :Pass each match through a function
n16           :  Convert to decimal
_          :  Pass through the following function, and convert back to hex
r17       :    Round to the nearest multiple of 17
Ã      :  End function
g     :  Get first character


# 8088 Assembly, IBM PC DOS, 59 58 bytes

Unassembled listing:

BE 0082     MOV  SI, 82H    ; SI to begining of input string
AC          LODSB           ; load first '#' char into AL
B4 0E       MOV  AH, 0EH    ; BIOS display char function
CD 10       INT  10H        ; call BIOS
B3 11       MOV  BL, 17     ; set up for divide by 17
B9 0304     MOV  CX, 0304H  ; hex byte loop counter (CH=3), shift counter (CL=4)
LOOP_BYTE:
B7 02       MOV  BH, 2      ; hex chars loop counter
LOOP_ALPHA:
2C 30       SUB  AL, '0'    ; convert from ASCII
3C 0A       CMP  AL, 10     ; is digit > 10 (A-F)?
2C 07       SUB  AL, 7      ; ASCII adjust alpha char to binary
NOT_ALPHA:
86 E0       XCHG AH, AL     ; swap first and second chars
FE CF       DEC  BH         ; decrement loop counter
75 F2       JNZ  LOOP_ALPHA ; loop to next hex char
D2 E0       SHL  AL, CL     ; shift low nibble to high nibble
32 E4       XOR  AH, AH     ; clear AH for add/division
05 0008     ADD  AX, 8      ; add 0.5 (8/16) to round (with overflow)
F6 F3       DIV  BL         ; divide by 17
3C 0A       CMP  AL, 10     ; is digit > 10?
04 07       ADD  AL, 7      ; binary adjust alpha char to ASCII
DISP_CHAR:
04 30       ADD  AL, '0'    ; convert to ASCII
B4 0E       MOV  AH, 0EH    ; BIOS display char function
CD 10       INT  10H        ; call BIOS
FE CD       DEC  CH         ; decrement loop counter
75 D4       JNZ  LOOP_BYTE  ; loop to next hex byte


Standalone PC DOS executable. Input is via command line, output is to console.

Most of the code length is handling the conversion of required hex string I/O into bytes, since DOS/x86 machine code has no built-ins for that.

I/O:

Download and test HEXCLR.COM, or xxd hexdump:

0000000: be82 00ac b40e cd10 b311 b904 03ad b702  ................
0000010: 2c30 3c0a 7c02 2c07 86e0 fecf 75f2 d2e0  ,0<.|.,.....u...
0000020: 02c4 32e4 0508 00f6 f33c 0a7c 0204 0704  ..2......<.|....
0000030: 30b4 0ecd 10fe cd75 d4c3                 0......u..


# JavaScript (ES6), 55 bytes

s=>s.replace(/\w./g,x=>(('0x'+x)/17+.5|0).toString(16))


Try it online!

• Nice use of toString! I didn't realize it could take a radix param. Jul 8, 2019 at 14:50

# Retina 0.8.2, 88 bytes

(\w)(.)
$1,$2;
[A-F]
1$& TLd \d+$*
+1,
,16$* , 8$*
(1{17})*1*;
$#1; TdL1\d B\B|;  Try it online! Link includes test cases. Explanation: (\w)(.)$1,$2;  Pair up the hex digits. [A-F] 1$&
TLd


Convert each digit separately to decimal.

\d+
$*  Convert each decimal digit to unary. +1, ,16$*


Finish the hexadecimal conversion of the pair of digits.

,
8$* (1{17})*1*;$#1;


Add 8 and divide by 17.

TdL1\d
B\B|;



# PHP, 75 67 bytes

#<?php for($m=3;$m;)echo dechex((hexdec($argn)>>--$m*8&255)/17+.5);


# Python 3, 7270 68 bytes

lambda x:'#'+''.join(f"{(int(x[i:i+2],16)+8)//17:X}"for i in(1,3,5))


Try it online!

This is a port of Grzegorz Oledzkis original answer, which I helped him golfing down.

Two features of Python 3 help us save bytes:

• Floating point division by default
• Format string literals

-2 bytes thanx to Jonathan Allan

• (int(x[i:i+2],16)+8)//17 saves 2 Jun 28, 2019 at 23:32

# Jelly, 20 19 bytes

ḊØHiⱮs2ḅ⁴+8:17ịØHṭḢ


Try it online!

# C# (Visual C# Interactive Compiler), 81 bytes

x=>"".Aggregate("#",(a,b)=>a+$"{(Convert.ToInt32(x[b]+""+x[b+1],16)+8)/17:X}")  Try it online! # Wolfram Language (Mathematica), 63 48 bytes "#"<>Round[15List@@RGBColor@#]~IntegerString~16&  Try it online! -15 bytes thanks to attinat! Replacing StringJoin with <> and compressing the syntax. 1. RGBColor@# converts the input string to a color of the form RGBColor[r, g, b] with three floating-point arguments in the range 0..1. 2. Round[15 List @@ %] multiplies the list of three arguments by 15 and rounds them to the nearest integer. We now have a list of three integer values corresponding to the three desired hexadecimal digits. 3. %~IntegerString~16 converts this list of three integers to a list of three hexadecimal strings of one character each. 4. "#"<>% prepends a # character and joins all these characters together. • 48 bytes – att Jun 30, 2019 at 2:27 # MathGolf, 19 12 bytes ╞2/¢8+F/¢'#▌  Output as character-list. If this is not allowed, an additional trailing y has to be added to join the character-list to a string. -7 bytes thanks to @maxb, since I looked past a builtin (2ô_2<\1>] to 2/). Try it online. Explanation: ╞ # Remove the first character from the (implicit) input-string 2/ # Split the string into parts of size 2 ¢ # Convert each part from hexadecimal to integer 8+ # Add 8 to each integer F/ # Integer-divide each integer by 17 ¢ # Then convert back from integer to hexadecimal '#▌ '# Prepend '#' in front of the list # (which is output implicitly as result)  ## Ruby (2.5.3), 45, 44, 42 bytes ->a{a.gsub(/\w./){|b|"%X"%((8+b.hex)/17)}}  EDIT: saved one byte because we don't need a character group for the second character in the regex (inspired by Neil's answer) EDIT 2: saved 2 bytes because the dash rocket lambda syntax doesn't need brackets around the argument • You can save 7 bytes by taking input on stdin and using the -p flag and another 2 by using $& instead of an argument inside the block: tio.run/##KypNqvz/… Jul 2, 2019 at 16:29
• @Jordan Thanks! I didn't know about either of those so that's a real help for future golfing attempts Jul 3, 2019 at 17:29

## Python 2 (109101978583 74 bytes)

lambda x:'#'+''.join(hex(int(int(x[i:i+2],16)/17.+.5))[2:]for i in[1,3,5])


The "nearest distance" is handled by division by 17 and rounding.

Improvements:

-8 bytes by using the int(...+.5) trick instead of int(round(...))

-4 bytes by using list comprehension instead of map()

-1 byte by hardcoding # in the output (thanks @movatica)

-10 bytes by not using re.findall("..",...) in favor of explicit String splicing

-2 bytes by not using list comprehension, but an inline generator expression in join (thanks @movatica)

-1 byte by not splicing the :7 ending for blue part

-9 bytes by better iteration over colors - i.e. iterating over indices, not actual characters (thanks @movatica)

• @movatica - you're right, added it Jun 28, 2019 at 21:36
• Save 1 byte by hardcoding '#' instead of x[0]. Jun 28, 2019 at 21:37
• You can skip the list comprehension inside ''.join(...), as it also handles a generator expression. Just remove the [] and save 2 more bytes :) Jun 28, 2019 at 21:42
• Thanks! range(1,6,2) is even better with [1,3,5] Jun 28, 2019 at 21:47
• Jonathan Allen proposed a different trick for rounding in mz Pzthon3 version. It applies here as well: lambda x:'#'+''.join(hex((int(x[i:i+2],16)+8)/17)[2:]for i in[1,3,5]) -> 69 bytes Jun 29, 2019 at 7:06

# Perl 5-p, 35 34 bytes

@nwellnhof saved a byte

s|\w.|sprintf'%X',.5+(hex$&)/17|ge  Try it online! Reads from STDIN, replaces each pair of items that is not # with the appropriate single character using the division by 17 method for finding the nearest, then implicitly outputs (-p) the result. # Python 3, 67 bytes f=lambda x:(f(x[:-2])if x[3:]else"#")+f'{(int(x[-2:],16)+8)//17:X}'  • Welcome. Consider adding a description, explanation, or link to an online interpreter, such as TIO where we can run your code. Code-only answers tend to get automatically flagged as low-quality. See other existing answers for examples. Jul 5, 2019 at 18:18 # Red, 103 bytes func[c][r: to 1 c to #1 rejoin reverse collect[loop 3[keep to-hex/size r % 256 + 8 / 17 1 r: r / 256]]]  Try it online! It turned out that the current Linux version of Red doesn't have an implementation of the hex-to-rgb function, that's why I make the base conversion "manually" :) # This works fine in the Red GUI console on Windows: # Red, 94 bytes f: func[c][r: hex-to-rgb c to #1 rejoin collect[repeat n 3[keep to-hex/size r/:n + 8 / 17 1]]]  # Perl 6, 35 bytes {S:g[\w.]=fmt :16(~$/)/17+.5: '%X'}


Try it online!

# Charcoal, 22 bytes

#Ｆ⪪⮌…⮌Ｓ⁶¦²⍘÷⁺⁸⍘ι¹⁶¦¹⁷φ


Try it online! Link is to verbose version of code. Explanation:

#                       Literal #
Ｓ                 Input string
⮌                  Reversed
…  ⁶                Truncated to length 6
⮌                    Reversed
⪪      ²              Split into pairs of characters
Ｆ                      Loop over each pair
ι        Current pair
⍘ ¹⁶      Convert from base 16
÷       ¹⁷   Integer divide by 17
⍘          φ  Convert to large base
Implicitly print


# R, 57 bytes

cat("#",sprintf("%x",(col2rgb(scan(,""))+8)%/%17),sep="")


Try it online!

# Pyth, 20 bytes

+\#sm.H/+8id16 17c3t


Try it here.

NOTE: In case the link above raises an ImportError, go here instead; there is currently a bug in the "official" page, and this is a temporary solution by Maltysen. This link may stop working after the official one is fixed.

# Forth (gforth), 87 bytes

: f d>s 1- hex ." #"3. do 2 + dup 2 s>number d>s 17 /mod swap 8 > - 1 .r loop decimal ;


Try it online!

### Explanation

1. Ignore/truncate first character of input (#)
2. Set interpreter to hexadecimal mode
3. Output #
4. Loop 3 times, in each loop:
2. Convert next 2 characters in string to a hexadecimal number
3. Use division and module by 17 (0x11) to get closest value for shortened component
4. Output with no preceding space
5. Set interpreter back to Decimal mode

### Code Explanation

: f                    \ start a new word definition
d>s                  \ convert double-length int to single-length (cheaper drop)
1- hex               \ subtract 1 from string address, set current base to 10
." #"                \ output #
3. do                \ start a loop from 0 to 2 (inclusive)
2 + dup            \ add 2 to string starting address and duplicate
2 s>number         \ parse the next 2 characters to a hexadecimal value
d>s                \ convert result to single-length value
17 / mod           \ get the quotient and remainder of dividing by 17
swap               \ move the remainder to the top of the stack
8 > -              \ if remainder is greater than 8, add 1 to quotient
1 .r               \ output result (as hexadecimal) with no space
loop                 \ end the loop
decimal              \ set interpreter back to base 10 (decimal)
;                      \ end the word definition


# Stax, 13 bytes

Ç┤!usτ╓♪╩⌂^÷◙


Run and debug it

# K4, 39 bytes

Solution:

"#",{x@_1%17%8+16/:x?y}[.Q.nA]@/:3 2#1_


Explanation:

Uses the same strategy as a lot of these answers (i.e. add 8, divide by 17):

"#",{x@_1%17%8+16/:x?y}[.Q.nA]@/:3 2#1_ / the solution
1_ / drop first character
3 2#   / reshape as 3x2 (e.g. "FF", "00", "00")
@/:       / apply each-right to left lambda
{                 }[     ]          / lambda with first argument populated
.Q.nA           / "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
x?y                  / get index of hex character, e.g. "AA" => 10 10
16/:                     / convert from base-16

• "#",{x@*16\:a?&/a:abs(17*!16)-16/:x?y}[.Q.nA]@/:3 2#1_ - my original idea for 54 bytes