# Convert height to US Men’s Jacket size

In the US, clothing size sometimes has rough designations like M, L, XXL, etc. The US designation of the "men's jacket" category depends on the height of the person. So, in this challenge, you should implement this conversion, which is defined by the following table, taken from this site:

US size Body height (cm)
XXXS    160 - 164
XXS     162 - 166
XS      166 - 170
S       168 - 173
M       171 - 176
L       174 - 182
XL      180 - 186
XXL     184 - 189
3XL     187 - 193
4XL     191 - 194
5XL     193 - 198
6XL     197 - 200


* I chose the "men's jacket" category from there because it has the most variability

### Input

Body height, in cm. As a number or a string.

### Output

Size designation - as a return value or printed.

• For some sizes, designation is uncertain (e.g. 174, 175 and 176 correspond to both M and L) - your converter should return one of these, deterministically or not
• If it's easier, output fully spelled versions like XXXXL instead of 4XL; however, 2XL and 2XS are invalid (apparently they are used in real life, but I decided to forbid them because I thought they weren't)
• Assume the input is valid, i.e. a whole number between 160 and 200
• Are the upper bounds included? (e.g. is XXXS a valid answer for 164?) Commented Nov 5, 2020 at 17:48
• It's a human-readable table; of course the upper bounds are included! I clarified the text. Commented Nov 5, 2020 at 17:54
• I think it's worth saying outright that the input is a whole number.
– xnor
Commented Nov 5, 2020 at 18:51
• Commented Nov 5, 2020 at 18:57
• "Output XXXL instead of 3XL if it's easier; however, 2XL and 2XS are invalid". Is this a specific example, or can 4XL, 5XL, and 6XL likewise be represented as XXXXL, XXXXXL and XXXXXXL? And can XXXS be returned as 3XS? Commented Nov 6, 2020 at 7:18

# Python 2, 5855524947 45 bytes

-13 bytes with great contributions from Arnauld (thanks!!!), xnor and xash.
Switched from Python 3 to Python 2 for an extra 2 bytes.

Tried to profit from the overlapping regions. You don't even need a size M, because the sizes of S and L don't have a gap between them. Luckily, the number of X's for S and L can be expressed as a linear integer expression.

lambda h:max(173-h>>2,h/3-60)*'X'+'SL'[h>173]


Try it online!

Just for fun (and for educational purposes) the original 58 bytes submission:

lambda h:(-h+173)//4*'X'+'S'if h<174else(h-180)//3*'X'+'L'

• I think the if/else can be selection from a two-element list.
– xnor
Commented Nov 5, 2020 at 21:55
• @xash : I’m sorry. I don’t see where/how I could use your suggestion... Could you please give me a hint? Probably I’m missing something... Also: as per Arnauld suggestion, I consider switching to Python2 because that saves 2 bytes... Commented Nov 6, 2020 at 8:35
• 49 bytes (this includes the suggestion from @xash) Commented Nov 6, 2020 at 9:43
• 45 bytes Commented Nov 6, 2020 at 11:05
• No worries. Let's update this one. (This is just some extra golfing still based on your initial method.) Commented Nov 6, 2020 at 15:46

# JavaScript (ES8), 50 bytes

This version is derived from the method used in Python by agtoever.

n=>'SL'[q=n/87>>1].padStart(q?n/3-59:177-n>>2,'X')


Try it online!

# JavaScript (ES6), 61 bytes

n=>'6543X LMS X3'[i=59-n/3.4|0].trim()+(i<6?'XL':i>8?'XS':'')


Try it online!

# Python 2, 82 bytes

lambda x:"3XXSMLXX3456XXS   LXXXXXSS     LLLLL"[~-x/3-53-x/177-x/189+(x==195)::12]


Try it online!

-2 bytes thanks to ovs

• You can save two bytes by changing (x>177) and (x>189) to integer division with x//178 and x//190.
– ovs
Commented Nov 5, 2020 at 19:17

# K (ngn/k), 36 bytes

{"XSL"(&0|(-4!x)|-3!-7-x),1+0>x}173-


Try it online!

Adapted by @ngn from @agtoever's python golf.

# K (ngn/k), 51 bytes

|$SXXXSXXSXMLLXLX3LX5LX6@(159+\7\8689119)'  Try it online! bins the input, using a reduced subset of the available sizes to reduce the byte count. • (...)' run a binary lookup of the (implicitly provided) height against the list of heights; this returns the index of the first value equal to or larger than the input • (159+\7\8689119) a compressed version of 160 163 166 171 177 183 187 193 198 • SXXXSXXSXMLLXLX3LX5LX6@ index into the list of sizes • |$ convert to string and reverse the characters

The sizes with heights unique to them are:

• XXXS => 160 161
• XXS => 165
• XS => 167
• L => 177 178 179
• XL => 183
• 3XL => 190
• 5XL => 195 196
• 6XL => 199 200

The only heights not covered by these sizes are 171 172 173, which can be covered by either S or M. M is preferable, as it minimizes the differences between successive items of the list of heights. The other cutoffs between sizes were similarly chosen.

# J, 49 bytes

M is not needed, and the Xs are written out.

(+/\157,6 7#4 3)&(('X'#~0>._1+5|@-I.),'LS'{~5>I.)


Try it online! Prints the table with all possible outputs. First column is the f(n), followed by n, followed by the possible sizes for n.

• 157,6 7#4 3: 157 4 4 4 4 4 4 3 3 3 3 3 3 3
• (+/\…)&f 157 161 165 169 173 177 181 184 187 190 193 196 199 202 are the intervals …,x_0], (x_0, x_1], (x_1, x_2], ….
• 'LS'{~5>I. get the interval index of n – if it is less than 5, then S, otherwise L.
• ('X'#~0>._1+5|@-I.), interval index, subtracted by 5 (…, XS is -1, S is 0, L is 1, XL is 2, …), absolute value, take max(n-1, n) to lower the L side by one. Take this amount of X's and prepend it to the S/L.

# 05AB1E, 37 36 bytes

Thanks to Kevin Cruijssen for -1 byte!

Looking at some answers in practical languages, this seems way too long.

‘—ŽS‘.sR…MLX6×«ηí)˜IŽ6lbS3+.¥Ƶz+›Oè


This uses the following lookup table:

XXXS    160 - 162
XXS     163 - 166
XS      167 - 170
S       171 - 173
M       174 - 176
L       177 - 179
XL      180 - 183
XXL     184 - 186
3XL     187 - 190
4XL     191 - 193
5XL     194 - 196
6XL     197 - 200


"SXXX"ηíR…MLX6×«ηí)˜ generates all possible outputs, IŽ6lbS3+.¥Ƶz+›Oè selects the correct one.

‘—ŽS‘                  # dictionary string   "XXXS"
.s                # take the suffixes   ["S", "SX", "SXX", "SXXX"]
R               # reverse the list    ["XXXS", "XXS", "XS", "S"]
…MLX           # push string literal "MLX"
          # push each character to the stack
6×        # repeat "X" 6 times  "XXXXXX"
«       # concat with "L"     "LXXXXXX"
η      # take prefixes       ["L", "LX", ..., "LXXXXXX"]
í     # reverse each        ["L", "XL", ..., "XXXXXXL"]
)    # put the entire stack into a list
˜   # and flatten the list

I                      # push the input
Ž6l                   # compressed integer  1577
b                  # convert to binary   "11000101001"
S                 # split into digits   [1,1,0,0,0,1,0,1,0,0,1]
3+               # add 3 to each digit [4,4,3,3,3,4,3,4,3,3,4]
.¥             # un-delta, cumulative sum with 0 prepended
Ƶz           # compressed integer  162
+          # add to each element
# [162,166,170,173,176,179,183,186,190,193,196,200]
›         # is each item larger than the input
O        # sum the results
è       # index into the list of possible outputs


A port of agtoevers's Python answer comes in at 28 25 bytes in the legacy edition:

Ƶи‹i'SƵвI-4ë'LIƵΔ-3}÷'X×ì


See all outputs!

Thanks to Kevin Cruijssen for -3 bytes!

• Could the fact that (in the question) the ranges for S and L completely cover M help remove any bytes? It seems as though most answers are exploiting that fact Commented Nov 6, 2020 at 3:31
• @cairdcoinheringaahing It currently costs me one byte to include M in the list of outputs, but in turn it allows me to have the upper bounds of the ranges to be 3 or 4 cm apart, which can be encoded in binary. If I drop the M I would need to encode this in base 4 which costs at least 4 additional bytes. With a different approach this might help (see the 28-byter), but not with the current one.
– ovs
Commented Nov 6, 2020 at 6:56
• "SXXX"ηí can be ‘—ŽS‘.s for -1 Commented Nov 6, 2020 at 8:16
• Also, the 28 bytes answer can be 25 by switching to the legacy version of 05AB1E, so you don't need the Dd* (you do have to swap the if-statement and use ‹ instead of @ though: 25 bytes - try all test cases. Commented Nov 6, 2020 at 8:21
• @KevinCruijssen thanks a lot, the behavior of × makes a lot more sense in the legacy version ;)
– ovs
Commented Nov 6, 2020 at 18:48

# Charcoal, 42 26 bytes

≔⁻Ｎ¹⁷³θ×X÷±θ⁴×X⊖⊖÷θ³§LS›⁰θ


Try it online! Link is to verbose version of code. Port of @Port of @agtoever's answer, so outputs using XXXL etc.. Explanation:

≔⁻Ｎ¹⁷³θ


Subtract 173 from the input.

×X÷±θ⁴


If it's negative then print a number of Xs equal to a quarter of the negated value.

×X⊖⊖÷θ³


If it's positive then print a number of Xs two less than a third of the value.

§LS›⁰θ


Output S for negative and L for positive. The resulting lookup table:

XXXS    160 - 161
XXS     162 - 165
XS      166 - 179
S       170 - 173
L       174 - 182
XL      183 - 185
XXL     186 - 188
XXXL    189 - 191
XXXXL   192 - 194
XXXXXL  195 - 197
XXXXXXL 198 - 200


Previous 42-byte answer using 3XL style output:

§⪪”{➙✳FＩ2κ？↓gＶＫ¤↘⸿⊙⁻QR”¶Σ÷Ｎ⁺¹⁶²Ｅ⁸Σ…426676ι


Try it online! Link is to verbose version of code. Explanation: Implements the following lookup table:

XXXS    160 - 161
XXS     162 - 165
XS      166 - 167
S       168 - 173
L       174 - 179
XL      180 - 186
3XL     187 - 192
5XL     193 - 196
6XL     197 - 200


XXXS is used for sizes of up to 161, and subsequent sizes depends on how many digits from the substring 426676 can added while the keeping the total less than the input, with 0 digits giving XXS and 7 digits (by repeating the initial 4) giving 6XL. (It's not possible to repeat additional digits as the 6th digit must be at least 6 but neither of the first two digits can be that high.)

# Python 3, 278277260 217 bytes

-1 byte thanks to @HyperNeutrino
-17 bytes thanks to @SunnyMoon
another -43 bytes thanks to @HyperNeutrino

lambda x:[c for a,b,c in[[160,165,"3XS"],[162,167,"XXS"],[166,171,"XS"],[168,174,"S"],[174,183,"L"],[180,187,"XL"],[184,190,"XXL"],[187,194,"3XL"],[191,195,"4XL"],[193,199,"5XL"],[197,201,"6XL"]]if x in range(a,b)][0]


Try it online!

• I think you can use 3XS instead of XXXS. Commented Nov 5, 2020 at 18:13
• I think you can remove ,[r(171,177),"M"], because M can also be either S or L. Commented Nov 5, 2020 at 18:16
• You can save 35 bytes by putting the range in the if: TIO Commented Nov 5, 2020 at 18:25
• you can then save an additional 8 bytes by turning it into a lambda using an in-line comprehension: TIO Commented Nov 5, 2020 at 18:28

# Perl 5, 39 bytes

$_=$_<174?X x(57-$_/3).S:X x($_/3-60).L


Try it online!

With all tests: Try it online!

Recognising that size M is not needed and that the number of X'es can be calculated from a difference divided by 3. Uses unquoted barewords for S, L and X.

# Husk, 2625 25 bytes

Edit: fixed bug, same bytes

§:oR'X?_-3<0o?'S'L<1-57/3


Try it online!

§:oR'X?_-3<0o?'S'L<1-57/3
-57/3  # x = (height/3)-57
§:                         # now join 2 strings:
o?'S'L<1       # string 1: 'S' if x<1, 'L' otherwise
ȯR'X                     # string 2: repeat 'X'
?_-3<0               # if x is positive: x-3 times
# if x is negative: -x times


# Groovy, 74 71 70 64 bytes

f={('XXXSXXSXSSMLXLXXL3XL4XL5XL6XL'=~/\d?X*./)[it/3.4-47as int]}


Try it online!

1. ('XXXSXXSXSSMLXLXXL3XL4XL5XL6XL'=~/\d?X*./) finds all matches of the regex against the string, i.e. [XXXS, XXS, XS, S, M, L, XL, XXL, 3XL, 4XL, 5XL, 6XL].
2. it/3.4-47as int converts the height to a valid index within the list of matches.
3. (string=~pattern)[index] retrieves the match at the given index

# Python 3, 85 bytes

lambda s:[b for*b,a in b"3XSB XXSD XSH SK LT XLX 3XL_ 5XLd 6XLf".split()if s-99<a][0]


Try it online!

Outputs as a list of ASCII code-points.

# Julia, 65 bytes

x->split("XXXS XXS XS M L XL XXL 3XL 5XL 6XL")[x.<162:4.3:204][1]


Try it online!

# Retina 0.8.2, 57 bytes

.+
$*1-174$*
(1+)-\1
-
-(1111)*1+
$#1$*XS
111
X
X?X?1*-
L


Try it online! Link includes test suite. Port of @agtoever's answer, so outputs using XXXL etc.. Explanation:

.+
$*1-174$*
(1+)-\1
-


Convert to unary and subtract 174.

-(1111)*1+
$#1$*XS


If the result is negative, then negate, subtract 1, and divide by 4, which gives the size in X*S.

111
X
X?X?1*-
L


Otherwise, divide by 3 and subtract up to 2, which gives the size in X*L.

The resulting lookup table:

XXXS    160 - 161
XXS     162 - 165
XS      166 - 179
S       170 - 173
L       174 - 182
XL      183 - 185
XXL     186 - 188
XXXL    189 - 191
XXXXL   192 - 194
XXXXXL  195 - 197
XXXXXXL 198 - 200


# Bash, 128 bytes

L=kfd90HK
H=uBgI0LP
s=$[$1-150]
c=L
((s<24))&&c=S
for ((;;i++)) {
(($[64#${L:i:1}]<s&&s<$[64#${H:i:1}]))&&echo $c&&exit c=X$c
}



Try it online!

Ungolfed version below for review:

# Size  cm range	-150	>Low	<High	# of X
#XXXS	160-163		10-13	9		3
#XXS	164-165		14-15	13	16	2
#XS	166-170		16-20	15		1
#S	171-173		21-23	20		0
#M
#L	174-179		24-29		30	0
#XL	180-186		30-36		37	1
#XXL
#3XL	187-193		37-43		44	3
#4XL
#5XL	194-196		44-46	43	47	5
#6XL	197-200		47-50	46	51	6

#L=20 15 13 9 0 43 46
#H=30 37 16 44 0 47 51
L=kfd90HK
H=uBgI0LP

# print the high and low threshold arrays to verify base64 numbers
for ((k=0;k<7;k++)) {
l=$[64#${L:k:1}]
h=$[64#${H:k:1}]
echo $k: low:$l high:$h } echo # Test set: check algorithm on all values [160-200] for ((j=160;j<=200;j++)) { s=$[j-150]

c=L;((s<24))&&c=S
for ((i=0;i<8;i++)) {
l=$[64#${L:i:1}]
h=$[64#${H:i:1}]
((l<s&&s<h))&&echo $j:$c&&break
c=X\$c
}
}

`

Try it online!