Coat of Many Colours
Challenge
Given a list of unique colour names as input, sort them in the order that they first appear in Joseph's Amazing Technicolour Dreamcoat.
Example
Input: green, blue, red, brown
Output: red, green, brown, blue
The full list of colours, in order, is:
1. red
2. yellow
3. green
4. brown
5. scarlet
6. black
7. ochre
8. peach
9. ruby
10. olive
11. violet
12. fawn
13. lilac
14. gold
15. chocolate
16. mauve
17. cream
18. crimson
19. silver
20. rose
21. azure
22. lemon
23. russet
24. grey
25. purple
26. white
27. pink
28. orange
29. blue
Or as an array of strings:
["red","yellow","green","brown","scarlet","black","ochre","peach","ruby","olive","violet","fawn","lilac","gold","chocolate","mauve","cream","crimson","silver","rose","azure","lemon","russet","grey","purple","white","pink","orange","blue"]
Rules
- You may take input by any reasonable, convenient means (e.g., an array of strings, a delimited string, individual strings), but please specify your input method in your answer.
- You may do the same for your output.
- The input will only ever contain colours from the above list.
- Your solution should be able to handle empty inputs.
- You may choose whether all words in the input are consistently uppercase, lowercase or title case but your output's casing must match your input's.
- This is code-golf so lowest byte count in each language wins.
- As always, standard loopholes are forbidden.
Test cases
Input: []
Output: []
Input: ["green", "blue", "red", "brown"]
Output: ["red", "green", "brown", "blue"]
Input: ["gold", "grey", "green"]
Output: ["green", "gold", "grey"]
Input: ["ruby","yellow","red","grey"]
Output: ["red", "yellow", "ruby", "grey"]
Input: ["gold", "green", "fawn", "white", "azure", "rose", "black", "purple", "orange", "silver", "ruby", "blue", "lilac", "crimson", "pink", "cream", "lemon", "russet", "grey", "olive", "violet", "mauve", "chocolate", "yellow", "peach", "brown", "ochre", "scarlet", "red"]
Output: ["red", "yellow", "green", "brown", "scarlet", "black", "ochre", "peach", "ruby", "olive", "violet", "fawn", "lilac", "gold", "chocolate", "mauve", "cream", "crimson", "silver", "rose", "azure", "lemon", "russet", "grey", "purple", "white", "pink", "orange", "blue"]
Scala, 119 bytes Saved 26 b …
5y ago
[JavaScript (Node.js)], 153 14 …
5y ago
[C (gcc)], 301 bytes Functi …
5y ago
C, 534 bytes Strictly confo …
5y ago
Python 3, 120 116 bytes a …
4mo ago
Perl, 111 bytes Function ta …
5mo ago
[Python 3], 349 261 160 bytes …
5y ago
Ruby, 101 79 76 72 bytes Th …
4y ago
8 answers
Scala, 119 bytes
Saved 26 bytes after porting Moshi's solution!
_ sortBy("y gree br sc bla oc pe rub ol v f li go ch m cre c s ro a l ru g pu w p o b"split " "indexWhere _.startsWith)
Original solution, 145 130 bytes
Saved 15 bytes after looking at Moshi's solution!
_ sortBy("d,ll,ee,ow,ar,ack,h,ac,b,iv,ol,w,la,ld,o,uv,ea,im,l,se,ur,m,s,e,r,i,n,a,u"split ","indexWhere _.substring(2).startsWith)
It turns out that after dropping the first 2 letters, the first 2 letters after that (and in some cases 1 or 3) are enough to identify colors.
_ sortBy( //Sort the input by this function:
"..." //Take the comma separated string of colors
.split(",") //Split on commas
indexWhere //And find the index where
_.substring(2) //The current string with the 1st 2 chars removed
.startsWith //Starts with that color prefix
)
0 comment threads
JavaScript (Node.js), 153 147 bytes
-6 bytes thanks to Shaggy!
a=>a.sort((x,y)=>g(x)-g(y),g=x=>"y gree br sc bla oc pe rub ol v f li go ch m cre c s ro a l ru g pu w p o b".split` `.findIndex(t=>!x.indexOf(t)))
Inspired by user's solution, I found the minimum number of initial characters that uniquely identified each color. I then golfed if further by realizing the ones at the end of the list don't actually have to uniquely identify the color, because the ones it couldn't determine would have been caught already. (E.g. green and grey need the gree to determine green, but if it isn't green then it must be grey and we can catch that with just g)
Old answer, 219 bytes
a=>"red yellow green brown scarlet black ochre peach ruby olive violet fawn lilac gold chocolate mauve cream crimson silver rose azure lemon russet grey purple white pink orange blue".split(' ').filter(i=>a.includes(i))
C (gcc), 301 bytes
Function taking in an array of null-terminated strings and the array's length.
#include <string.h>
#include <stdlib.h>
char*s="edellowreenrowncarletlackchreachubyliveioletawnilacoldhocolateauvereamrimsonilverosezuremonussetreyurplehiteinkrangelue";int g(const void*a,const void*b){return strstr(s,*(char**)a+1)-strstr(s,*(char**)b+1);}void f(char**a,int l){qsort(a,l,sizeof*a,g);}
I shamelessly stole the idea of removing the first two characters from user's solution. Note: My original solution had a bug where the wn from fawn matched the own from brown and broke the order. This updated solution only removes the first character, making rown and awn distinct.
1 comment thread
C, 534 bytes
Strictly conforming program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char i=1,**c,**d,*t[30]={"red","yellow","green","brown","scarlet","black","ochre","peach","ruby","olive","violet","fawn","lilac","gold","chocolate","mauve","cream","crimson","silver","rose","azure","lemon","russet","grey","purple","white","pink","orange","blue"};int f(const void*a,const void*b){c=d=t;for(;*c;)if(!strcmp(*c++,*(char**)a))break;for(;*d;)if(!strcmp(*d++,*(char**)b))break;return c<d?-1:c!=d;}int main(int c,char**v){qsort(v+1,c-1,8,f);for(;i<c;)puts(v[i++]);}
The char* casts are a bit questionable in a strictly conforming program since they remove const qualifiers. My argument for why it is valid, is that the effective type of the argv strings ought to be char[].
I couldn't figure out how to use massive amounts of command line arguments on tio.run, here's a Godbolt with the worst test case entered: https://godbolt.org/z/vbdYe6cxx
3 comment threads
Python 3, 349 261 160 bytes
a="re y gree br sc bla oc pe rub ol v f li go ch m cre cri si ro a le rus grey pu w pi or blu".split()
def f(b):return[j for i in a for j in b if j[:len(i)]==i]
Golfed 88 bytes thanks to @user's advice. Golfed 81 bytes thanks to @celtschk's advice.
3 comment threads
Perl, 111 bytes
Function taking array of lowercase strings as argument and returning a sorted version.
sub J{map{$k=lc;grep{!index$_,$k}@_}ReYGreeBrScBlaOcPeRubOlVFLiGoChMCreCriSiRoALeRusGreyPuWPiOrBlu=~/.[a-z]*/g}
- the unique prefixes stored in order as bare string
- titlecase to avoid separate delimiter
- split them into a list
- loop over the elements
- emit any values from the input that match
Inefficient algorithm but 65 bytes shorter than my attempt at a proper sort using a Schwartzian transform.
standalone, 125 bytes
Takes space-delimited list on stdin. Writes space-delimited list to stdout.
perl -aE'$,=" ";say map{$k=lc;grep{!index$_,$k}@F}ReYGreeBrScBlaOcPeRubOlVFLiGoChMCreCriSiRoALeRusGreyPuWPiOrBlu=~/.[a-z]*/g'
- same core as above
-
-a- the values to sort are loaded from array@Finstead of@_
0 comment threads
Python 3, 120 116 bytes
a=b"F99%T / 9#mQ 9 %>^/%O/#*c 4bJLlHIgwY[NXrEdSsheZ]"*3
def f(l):l.sort(key=lambda s:a.find(len(s)+a[s[0]]+a[s[1]]))
Takes input as a list of bytes, representing lower-case colour names encoded in UTF-8 (or ASCII). Mutates the list by sorting it in-place. Try it online!
Explanation
I ran the list through gperf version 3.1, which found the following perfect hash function:
asso_values = {
'a' => 25, 'b' => 25, 'c' => 5, 'e' => 0,
'f' => 15, 'g' => 0, 'h' => 25, 'i' => 3,
'l' => 0, 'm' => 25, 'o' => 5, 'p' => 30,
'r' => 15, 's' => 5, 'u' => 15, 'v' => 3,
'w' => 10, 'y' => 0, 'z' => 20,
}
fn hash(s: str) -> int {
len(s) + asso_values[s[1]] + asso_values[s[0]]
}
wordlist = {
[ 5] = "lemon", "yellow",
[ 8] = "lilac", "gold", "olive",
[12] = "violet",
[14] = "silver", "ochre",
[17] = "scarlet", "red", "grey", "green",
[24] = "rose", "cream", "orange", "crimson",
[29] = "blue", "black",
[34] = "ruby", "peach", "russet", "pink",
[39] = "chocolate", "white",
[44] = "fawn", "brown",
[50] = "azure", "purple",
[55] = "mauve",
}
This hash function still works if we give everything a constant offset. I picked 64, which is twice the codepoint of the Unicode SPACE character. asso_values does not contain 2, so this allows us to represent it as a bytestring (where . represents an unused value):
a = b'.'*97 + b"99%. / 9#.. 9 %>./%./#*. 4"
Then – using bytes as input, so we don't need to call ord –, the hash function becomes:
def hash(s: bytes) -> int:
return len(s) + a[s[1]] + a[s[0]]
Now, we just need to map each hash key to its position in the song. We could subtract 69 (the smallest hash key, = 5 + 64), and then look them up in another sparse array; however, since we only care about the order (they do not need to map to particular values), we can store them more compactly as a string:
b"RFTmQ^OcbJLlHIgwY[NXrEdSsheZ]"
(Serendipitously, "red" maps to R, and "ochre" maps to O.)
Now, to find the relative position of each word, we can index the string with str.find: the worse-but-shorter version of str.index, which returns -1 when it can't find the value. Since -1 < 0, we can actually remove R from the string entirely!
We could store this as a separate constant, but – again – we only care about the order. And we've got a lot of unused space in our existing string… Interleaving them, we can get the string down to 48 characters, repeated 3 times.
The rest of the code is a straightforward call to list.sort using an anonymous function (lambda). I'm annoyed by how verbose this part is, but I don't see a way to cut it down.
I'm sure there's plenty of room for golfing, starting with a better perfect hash function: choosing different indices may allow us to eliminate the len dependency, and adding a subtraction or a constant offset may allow us to share characters between the hash table and the key order (rather than just interleaving the data). For now, though, I'm satisfied.
0 comment threads
Ruby, 101 79 76 72 bytes
This challenge was one of the funniest I have ever solved!
72 bytes solution (I show the solution as a Ruby string - because binary data is filtered out)
"->l{l.sort_by{'d\v\x162\x82\r\x1D\nJ\"\x01T\x0E?\x8B.\x11\x05\x06G*(\fM\x00 HyI'.index''<<_1[2,4].sum%145}}"
Unfortunately to make it work on Attempt This Online website, I had to generate code there and use eval (because if I paste code directly then it corrupts my code):
Attention This code suppose to include a binary data, which is filtered out by this website. Copying it from this website wont't work. You can clone the repository, which contains code (and generator) from github gist.

0 comment threads