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.


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



  • 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.



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


# "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)
  • \$\begingroup\$ agg:ra:va::ting:: broke mine heh... Ironic. \$\endgroup\$ Commented Nov 17, 2017 at 22:05

10 Answers 10


Husk, 11 bytes


Try it online!


ΣÖ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
  • \$\begingroup\$ There are a couple of really clever ideas here, you should add an explanation so people can appreciate them :D \$\endgroup\$
    – Leo
    Commented Nov 15, 2017 at 21:25
  • 1
    \$\begingroup\$ @Leo Added a breakdown of the code. \$\endgroup\$
    – Zgarb
    Commented Nov 15, 2017 at 21:43

Retina, 47 bytes


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.

  • \$\begingroup\$ 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). \$\endgroup\$ Commented Jan 18, 2019 at 9:47
  • 1
    \$\begingroup\$ @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 \$\endgroup\$ Commented Jan 18, 2019 at 10:00
  • \$\begingroup\$ Ah, nice. Would have never thought of that myself.. Thanks a lot. I've been able to fix my answer now. :) \$\endgroup\$ Commented Jan 18, 2019 at 10:17

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.


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


Try it online!

Full program.


JavaScript (ES6), 93 bytes

<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!

  • \$\begingroup\$ Each capital comes before its lowercase. E.g. 'AaBb'. I think changing =~/A-Z/ to ==/A-Z/ should fix the problem. \$\endgroup\$ Commented Nov 15, 2017 at 21:40

Haskell, 124 bytes

import Data.List
import Data.Char
s(c:x)|(l,r)<-span(==':')x=(-length l,toLower c,c:l):s r;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`$p`p $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!


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.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.