# Sort an Option String

## Background

In C, there is a function to help parse command-line arguments: getopt.

Getopt takes the argument count (argc), the argument list (argv), and a string representing the options the program should take (optstring).

The option string (optstring) is a series of option characters each followed by one colon (for an option requiring an argument), two colons (for an option with an optional argument), or zero colons (for an option without an argument).

For example: one might use the following option string for their company's new widget.

"v::x:hy:S:G::dns:f::"


But that looks horrible! The options are all mixed together! This option string would look much better if it were sorted:

"f::G::v::S:s:x:y:dhn"


## Specifications

• The input will be a string matching the regular expression ([A-Za-z]:{0,2})+

• You should take input as a string or list of characters.

• You should output the string sorted by the method detailed below.

• You do not need to handle empty input.

• You do not need to handle input with repeated options of the same type ("aa" is not valid, but "aa:a::" is valid).

### Sorting Method

In the sorted option string:

• The characters followed by two colons come first, sorted alphabetically, with uppercase letters before their respective lowercase letters (i.e. "aBb", not "Bab").

• Then are the characters with one colon, sorted alphabetically, with uppercase letters before their respective lowercase letters.

• Last are the characters with no colon, sorted alphabetically, with uppercase letters before their respective lowercase letters.

## Scoring

This is , so shortest answer in bytes in each language wins.

## Examples

# "input" => "output"
"v::x:hy:S:G::dns:f::" => "f::G::v::S:s:x:y:dhn"
"aa:a::" => "a::a:a"
"B:" => "B:"
"Confusio:n::" => "n::o:Cfinosu"
"Confusio:n:" => "n:o:Cfinosu"
"m:qW:X:xc:vViI" => "c:m:W:X:IiqVvx"
"V:omo:V" => "o:V:moV"
"agg:ra:va::ting::" => "a::g::a:g:aginrtv"
"input" => "inptu"


Long test case (312 characters)

Invaid Inputs (you don't have to handle):

"gg" (duplicated option)
"" (empty string)
":w:" (first colon isn't after a character)
"x:y:0:fhn" ('0' is not alphabetic)
"A:::" (3 colons is invalid)

• agg:ra:va::ting:: broke mine heh... Ironic. – Magic Octopus Urn Nov 17 '17 at 22:05

# Husk, 11 bytes

ΣÖom_↔Oġλ¬√


Try it online!

## Explanation

ΣÖom_↔Oġλ¬√  Implicit input, say "kH::r:R:Hk:"
ġ     Split between characters for which this gives a falsy result:
λ      Take two arguments, ignore the first.
√    Is the second argument a letter?
¬     Negate.
Now we have the options: ["k","H::","r:","R:","H","k:"]
O      Sort: ["H","H::","R:","k","k:","r:"]
Ö           Sort by
↔       reversing and
om_        converting everything to lowercase: ["H::","k:","R:","r:","H","k"]
Σ            Concatenate: H::k:R:r:Hk

• There are a couple of really clever ideas here, you should add an explanation so people can appreciate them :D – Leo Nov 15 '17 at 21:25
• @Leo Added a breakdown of the code. – Zgarb Nov 15 '17 at 21:43

## Retina, 47 bytes

.
$&$&
T01Ll..
O^\w.:*
.(.)
$1 O$^\w:*
$.&  Try it online! The trickiest part was actually sorting the letters, because Retina's "sort-by" feature doesn't have a case-insensitive sort (so by default it sorts the upper case letters in front of all lower case letters). To work around this, I had to insert a lower-case copy of each character to do the sorting and then get rid of it again. • Has a case-insensitive sort been added somewhere in the last year? And if not, is there a way to sort case-insensitive, but retain original order? I.e. tEsTiNg with your above sorting method sorts the pairs of letters to eEggiinNsstTtt → EgiNsTt instead of eEggiinNsstttT → EgiNstT  (the uppercase letters are always placed before the lowercase letters). – Kevin Cruijssen Jan 18 '19 at 9:47 • @KevinCruijssen You can do it with sort-by now, because you can easily transform the character to lowercase in the substitution string. tio.run/##K0otycxLNPyvquGe8N9fJUGPSyVHRe3//xLX4pBMv3QA – Martin Ender Jan 18 '19 at 10:00 • Ah, nice. Would have never thought of that myself.. Thanks a lot. I've been able to fix my answer now. :) – Kevin Cruijssen Jan 18 '19 at 10:17 # Python 2, 100 90 bytes lambda s:''.join(sorted(re.findall('.:*',s),key=lambda s:(-len(s),s.lower(),s))) import re  Try it online! # Jelly, 14 bytes n”:œṗ⁸ṢŒuÞṚLÞṚ  Try it online! Full program. # Japt, 17 16 bytes f".:*" ñ_iZw vÃq  Test it online! ### How it works Say the input string is v:vg::G:G::o:. The first part of the code, f".:*", finds all matches of the regex /.:*/g, which matches a character followed by any number of colons. This splits the input into [v:, v, g::, G:, G::, o:]. The ñ method on an array sorts each the array as if certain changes were applied, without actually applying them (like sorted(array, key=<whatever>) in Python). So we need to sort: • double-colons before everything else; • single-colons after double-colons, but before no-colons; • letters in their alphabetical order; and finally • uppercase letters before lowercase. Z{ZiZéJ v} is the shortest way I could find to do this:  Zw Reverse Z. v Convert to lowercase. Zi Insert this at the beginning of Z.  This is easiest to understand with some examples:  v: v g:: G: G:: o: Zw :v v ::g :G ::G :o v :v v ::g :g ::g :o Zi :vv: vv ::gg:: :gG: ::gG:: :oo:  The original values are then sorted lexicographically by these keys. The result is that ::gG:: is sorted to the front, then ::gg::, and so on, giving us [G::, g::, G:, o:, v:, v], which is then joined with q and outputted. ## JavaScript (ES6), 93 bytes f= s=>s.match(/\w:*/g).sort((a,b)=>b.length-a.length||parseInt(a,36)-parseInt(b,36)||a>b).join <input oninput=o.textContent=this.value&&f(this.value)><pre id=o> # Perl 5, 69 + 1 (-n) = 70 bytes say sort{$b=~y///c<=>length$a||($"=lc$a)cmp lc$b||1-2*($"ne$a)}/.:*/g


Try it online!

• Each capital comes before its lowercase. E.g. 'AaBb'. I think changing =~/A-Z/ to ==/A-Z/ should fix the problem. – pizzapants184 Nov 15 '17 at 21:40

import Data.List
import Data.Char
s(c:x)|(l,r)<-span(==':')x=(-length l,toLower c,c:l):s r;s _=[]
t(_,_,x)=x
f=(t=<<).sort.s


Try it online!

# Bash, 232 213 bytes

s(){ s=${1#?} c=${1%$s} o=${s%%[^:]*} s=${s#$o} o=$c$o;};
p()(s $1;l= g= p=$o d=$c;[[$o ]]&&(while s $s;[[$o ]];do
(((x=${#o}-${#p},y=36#$d-36#$c,x?x:y?y:62#$c-62#$d)>0))&&g+=$o||l+=$o;done;echo p $g$pp \$l))


try it online

# C# (.NET Core), 152 bytes

s=>{var o="";for(var d=":::";d!="";){d=d.Substring(1);for(var c='@';c!=90;o+=s.IndexOf(c+d)>=0?c+d:"",s=s.Replace(c+d,""),c^=' ')if(c<97)c++;}return o;}


Try it online!

Ungolfed:

s => {
var o = "";
for (var d = ":::"; d != "";) {
d = d.Substring(1);
for (
var c = '@';
c != 90;
o += s.IndexOf(c + d) >= 0 ? c + d : "", s = s.Replace(c + d, ""), c ^= ' '
) if (c < 97) c++;
}
return o;
}
`

The basic idea is to iterate over all 156 possible options and check for their existence. If found, append to the output, and remove from the input.