Sort a string, sort of

29

1

If you sort a string you'll typically get something like:

         ':Iaaceeefggghiiiiklllllmnnooooprrssstttttuuyyyy

Yes, that was the first sentence sorted.

As you can see, there are a lot of repeated characters, aa, eee, ttttt, 9 spaces and so on.

If we add 128 to the ASCII-value of the first duplicate, 256 to the second, 384 to the third and so on, sort it again and output the new string (modulus 128 to get the same characters back) we get the string:

 ':Iacefghiklmnoprstuy aegilnorstuy egilosty iloty lt    

(Note the single leading space and the 4 trailing spaces).

The string is "sequentially sorted" <space>':I....uy, <space>aeg....uy, <space>egi....ty, <space>iloty, <space>lt, <space>, <space>,<space>, <space>.

It might be easier to visualize this if we use a string with digits in it. The string 111222334 will when "sorted" be: 123412312.

Challenge:

To no surprise, the challenge is to write a code that sorts a string according to the description above.

You can assume that the input string will contain only printable ASCII-characters in the range 32-126 (space to tilde).


Test cases:

**Test cases:**
 *:Tacest*es*s*

If you sort a string you'll typically get something like:
 ':Iacefghiklmnoprstuy aegilnorstuy egilosty iloty lt    

Hello, World!
 !,HWdelorlol

#MATLAB, 114 bytes
 #,14ABLMTbesty 1A

f=@(s)[mod(sort(cell2mat(cellfun(@(c)c+128*(0:nnz(c)-1),mat2cell(sort(s),1,histc(s,unique(s))),'un',0))),128),''];
'()*+,-0128:;=@[]acdefhilmnoqrstuz'(),0128@acefilmnorstu'(),12celmnostu'(),12celnstu(),clnst(),cls(),cs(),()()()()

This is , so the shortest code in each language counted in bytes will winref.

Stewie Griffin

Posted 2017-01-10T21:19:54.400

Reputation: 43 471

Title is a bit confusing, resulting in me thinking this and ignoring the description: https://tio.run/nexus/05ab1e#@1@td2jh4ZVe//@rW3kmJqempWdkZufk5uUXFBWXlFYqJKamZ@bk5UM4IHZ@cUmlApACkjklCgoKAA Nice challenge otherwise, I'll work on expanding that to meet the brief.

– Magic Octopus Urn – 2017-01-10T21:40:48.867

Can we output a list of characters instead of a string? – Post Rock Garf Hunter – 2017-01-10T21:52:51.653

If you can input a string, then the output should be a string too. If a list of characters is the normal way of inputting and outputting strings in your languages then it's OK. You can for instance not output {'S', 'g', 'i', 'n', 'r', 't'} in Python, since the "normal" way to do it is "String". – Stewie Griffin – 2017-01-10T21:54:52.770

I'll correct my comment above: a string is a list of characters, so a list of characters is accepted output. However, a list of strings is not accepted. This means, if it's possible to add a second character to an element in your list then it's not accepted. As an example: {'a','b'} is not accepted in Matlab since you can add a character to each of the characters like this: {'aa','b'}. Your input and output must be on the same format. – Stewie Griffin – 2017-01-10T23:40:53.673

@StewieGriffin When you say sorted according to the description above. Do you mean my sort algorithm must follow the process of modifying ASCII values or it just has to produce the same output as that algorithm? – George Reith – 2017-01-11T00:01:21.137

No, you only need the same result. So far, I think only one answer has used the same algorithm. – Stewie Griffin – 2017-01-11T00:14:54.680

Proposed alternate title: ,Safginorst orst ort :P – James – 2017-01-12T18:03:17.087

@DJMcMayhem haha, nice and descriptive title... It's actually possible to pronounce, so it could work :) – Stewie Griffin – 2017-01-12T22:21:44.940

It would be funny if someone made an answer that was already sorted |:) – ckjbgames – 2017-02-03T18:34:10.823

Answers

15

Pyth, 5 bytes

s.T.g

Test suite

Very straightforward: Group and sort, transpose, concatenate.

s.T.g
s.T.gkQ    Implicit variables
   .gkQ    Group the input input lists of elements whose values match when the
           identity function is applied, sorted by the output value.
 .T        Transpose, skipping empty values. This puts all first characters into
           a list, then all second, etc.
s          Concatenate.

isaacg

Posted 2017-01-10T21:19:54.400

Reputation: 39 268

Pyth has everything to become new J, it's awesome – shabunc – 2017-01-12T01:36:41.283

3

@shabunc If you want to see the new J, check out https://github.com/DennisMitchell/jelly

– isaacg – 2017-01-12T04:51:34.170

14

Jelly, 3 bytes

ĠZị

Try it online!

How it works

Oh boy, this challenge was all but made for Jelly.

The group atom (Ġ) takes an array1 as input and groups indices that correspond to identical elements of the array. The array of index groups is sorted with the corresponding elements as keys, which is precisely the order we require for this challenge.

Next, the zip atom (Z) transposes rows and columns of the generated (ragged) matrix of indices. This simply consists of reading the columns of the matrix, skipping elements that are not present in that column. As a result, we get the first index of the character with the lowest code point, followed by the first index of the character with the second lowest code point, … followed by the second index of the character with the lowest code point, etc.

Finally, the unindex atom () retrieves the elements of the input array at all of its indices in the generated order. The result is a 2D character array, which Jelly flattens before printing it.


1 Jelly doesn't have a string type, just arrays of characters.

Dennis

Posted 2017-01-10T21:19:54.400

Reputation: 196 637

"Oh boy, this challenge was all but made for Jelly." -> 3 byte answer – geisterfurz007 – 2017-01-12T11:29:50.300

As I said, almost made for Jelly. :) – Dennis – 2017-01-12T12:54:36.340

10

Python 3, 109 105 104 103 99 93 90 88 81 79 69 bytes

2 bytes saved thanks to FlipTack

7 bytes saved because flornquake caught my dumb error

2 bytes saved thanks to xnor

10 bytes saved thanks to Dennis

a=[*input()]
while a:
    for c in sorted({*a}):print(end=c);a.remove(c)

Explanation

We start by converting our string to a list using a splat and storing that list in a variable a. Then while our a is not the empty list we go through each unique member of a in sorted order, print it and remove a copy of that character from the list.

Each iteration prints thus prints one copy of each character present in a.

Post Rock Garf Hunter

Posted 2017-01-10T21:19:54.400

Reputation: 55 382

Why is sorted even needed here? s=set(a) results in a sorted set of unique characters right? Then shouldn't list(s) also be sorted? (I have tested, so I know it's not, I just don't understand why) – Stewie Griffin – 2017-01-10T22:02:59.530

1@StewieGriffin set is an unsorted set. – FlipTack – 2017-01-10T22:03:16.170

2@StewieGriffin when printed they are sorted but not by their ASCII values exactly. It often appears they are but I believe they are sorted by some type of hash. – Post Rock Garf Hunter – 2017-01-10T22:05:40.783

I see, I'm reading about it now... :) A bit confusing, since list gets a different order back... Why not keep it consistent at least... – Stewie Griffin – 2017-01-10T22:09:53.220

@WheatWizard If the loop body has constructs like if, while, for, etc, then it has to be on a newline. But if it's just a few statements, it can be written all on one line – FlipTack – 2017-01-10T22:17:08.187

1You can make f a string instead of a list to save a few bytes. – flornquake – 2017-01-11T03:08:37.923

@flornquake Thanks I should have seen that a while ago. – Post Rock Garf Hunter – 2017-01-11T03:13:46.057

1If you take a=list(input()), you can do a.remove(c), which is a net savings. – xnor – 2017-01-11T03:43:04.773

@Dennis Wow. I tried a while ago and even with the fancy splats I could only save a byte. Thanks for telling me. – Post Rock Garf Hunter – 2017-01-11T04:22:29.627

6

Haskell, 44 bytes

import Data.List
concat.transpose.group.sort

Usage example:

Prelude Data.List> concat.transpose.group.sort $ "If you sort a string you'll typically get something like:"
" ':Iacefghiklmnoprstuy aegilnorstuy egilosty iloty lt    "

Sort, group equal chars to a list of strings (e.g. "aabbc" -> ["aa","bb","c"]), transpose and flatten into a single string, again.

nimi

Posted 2017-01-10T21:19:54.400

Reputation: 34 639

6

Python 2, 75 bytes

lambda s:`zip(*sorted((s[:i].count(c),c)for i,c in enumerate(s)))[1]`[2::5]

Try it online!

Dennis

Posted 2017-01-10T21:19:54.400

Reputation: 196 637

1Don't know if it's valid but lambda s:`[sorted((1e9+s[:i].count(c),c)for i,c in enumerate(s))]`[18::21] works for strings with max length 9e9. – xnor – 2017-01-11T04:22:07.117

@xnor you can drop the [] and change 18 to 17 to save two bytes. lambda s:\sorted((1e9+s[:i].count(c),c)for i,c in enumerate(s))`[17::21]` – Post Rock Garf Hunter – 2017-01-11T04:38:00.600

@xnor At the very least, this should be a valid 32-bit Python golf. I tried to get rid off the zip, but I don't think adding 1e9 would have ever occurred to me... Thanks! – Dennis – 2017-01-11T04:45:04.020

@WheatWizard Good eye. Thanks! – Dennis – 2017-01-11T04:45:52.877

This fails if the string has backslashes in it. – Lynn – 2017-01-12T03:02:28.383

@Lynn Right, rollang back. Thanks for catching that. – Dennis – 2017-01-12T03:04:54.563

4

C, 109 106 105 104 102 100 97 98 96 91 Bytes

Back up to 98 Bytes, needed to initialize j to make f(n) re-useable

Down to 96 Bytes using puts in place of strlen B-)

It's strange I had to back to strlen but I got rid of the for(;i++;) loop so now it's down to 91 Bytes. Apparently the man page for puts reads;

"RETURNS
   If successful, the result is a nonnegative integer; otherwise, the result is `EOF'."

... I was lucky it was working in the first place

char*c,i,j;f(m){for(j=strlen(m);j;++i)for(c=m;*c;c++)if(*c==i){*c=7,putchar(i),j--;break;}}

test code...

main(c,v)char**v;
{
    char test[] = "If you sort a string you'll typically get something like: ";
    char test2[] = "Hello, World!";

    f(test);puts("");    
    f(test2);puts("");    
}

Here are a few test cases, now it's time to golf this down

C:\eng\golf>a.exe
 ':Iacefghiklmnoprstuy aegilnorstuy egilosty iloty lt
 !,HWdelorlo

cleblanc

Posted 2017-01-10T21:19:54.400

Reputation: 3 360

Are the trailing spaces left out in the first test case? – Stewie Griffin – 2017-01-10T22:07:41.597

I have three trailing spaces in the first test case... That's because I didn't include the trailing space on the input string ;-) – cleblanc – 2017-01-10T22:10:07.230

4

Dyalog APL, 21 chars = 39 bytes

t[0~⍨∊⍉(⊢⌸t)[⍋∪t←⍞;]]

t[...] index t (to be defined shortly) with...

0~⍨ zeros removed from

 the enlisted (flattened)

 transposed

(⊢⌸t)[...;] keyed* t, row-indexed by...

   the indices which would sort

   the unique letters of

  t←t, which has the value of

   prompted text input

TryAPL online!


⊢⌸t creates a table where the rows (padded with zeros for a rectangular table) list each unique letters' indices in t.

Adám

Posted 2017-01-10T21:19:54.400

Reputation: 37 779

1which of the glyphs are more expensive? – ren – 2017-01-11T06:39:01.890

1@wptreanor causes the entire thing to be UTF-8 instead of one byte per char. – Adám – 2017-01-11T07:16:55.477

4

Mathematica, 68 60 59 bytes

Split[Characters@#~SortBy~ToCharacterCode]~Flatten~{2}<>""&

Accepts a String. Outputs a String.

If list of characters were allowed (46 bytes):

Split[#~SortBy~ToCharacterCode]~Flatten~{2,1}&

Version using Sort (40 bytes):

Split@Sort@Characters@#~Flatten~{2}<>""&

This version cannot be my answer because Sort cannot be used here; Sort sorts by canonical order, not by character code.

JungHwan Min

Posted 2017-01-10T21:19:54.400

Reputation: 13 290

I don't know mathematica so this might be just fine, but did you read this comment?

– Stewie Griffin – 2017-01-10T23:22:38.163

@StewieGriffin Welp, nope. I can fix that, but doesn't that give an unfair advantage to languages that don't have a String vs Char[] distinction? Related meta discussion

– JungHwan Min – 2017-01-10T23:29:27.310

Good point. I made a correction, see the comment below the original. Fair? I'm not sure if this makes your answer valid or not. – Stewie Griffin – 2017-01-10T23:42:02.290

@StewieGriffin Mathematica doesn't have a distinction between characters and strings. Even the Characters command technically output a list of length-1 strings. – JungHwan Min – 2017-01-10T23:58:49.883

1

@StewieGriffin I think this is also relevant. I think it's better to allow the input in any reasonable format, be it a string, list of length 1 strings, array of characters, array of bytes, etc.

– ngenisis – 2017-01-11T01:47:30.500

Wow, I had no clue that this usage of Flatten exists. – Martin Ender – 2017-01-11T12:15:03.883

@MartinEnder This is a good guide.

– JungHwan Min – 2017-01-11T15:50:03.863

3

JavaScript (ES6), 79 bytes

f=s=>s&&(a=[...new Set(s)]).sort().join``+f(a.reduce((s,e)=>s.replace(e,``),s))
<input oninput=o.textContent=f(this.value)><pre id=o>

Works by extracting the set of unique characters, sorting it, removing them from the original string, and recursively calculating the sort of the rest of the string. 81 byte solution that I found interesting:

f=s=>s&&(s=[...s].sort().join``).replace(r=/(.)(\1*)/g,"$1")+f(s.replace(r,"$2"))

Neil

Posted 2017-01-10T21:19:54.400

Reputation: 95 035

3

Python 2, 77 76 bytes

d={}
def f(c):d[c]=r=d.get(c,c),;return r
print`sorted(input(),key=f)`[2::5]

Takes a quoted string as input from stdin.

Try it online!

flornquake

Posted 2017-01-10T21:19:54.400

Reputation: 1 467

I think this isn't allowed because functions have to be re-usable. You could make it a program.

– xnor – 2017-01-11T03:33:25.873

I really like this method, sorting with a function that mutates. The nesting of tuples is clever too. – xnor – 2017-01-11T04:10:45.857

@xnor Thanks, fixed. – flornquake – 2017-01-11T09:20:39.833

3

J, 16 15 bytes

/:+/@(={:)\;"0]

This is a verb that takes and returns one string. Try it online!

Miles saved a byte, thanks!

Explanation

Nothing too fancy here: sort primarily by order of occurrence, secondarily by char value.

/:+/@(={:)\;"0]  Input is y.
          \      Map over prefixes:
  +/              Sum
    @(   )        of
      =           bit-array of equality
       {:         with last element.
                 This gives an array of integers whose i'th element is k
                 if index i is the k'th occurrence of y[i].
           ;     Pair this array
            "0   element-wise
              ]  with y
/:               and sort y using it as key.

Zgarb

Posted 2017-01-10T21:19:54.400

Reputation: 39 083

I think you can save a byte moving summation to the outside of the parentheses +/@(={:)\ – miles – 2017-01-12T09:15:23.340

@Miles Oh yeah, because a train has infinite rank. Nice, thanks! – Zgarb – 2017-01-12T09:19:32.200

3

Mathematica, 55 bytes, non-competing

(Sort@Characters@#//.{a___,b_,b_,c___}:>{a,b,c,b})<>""&

Edit: Unfortunately, Mathematica's sort is not by character codes, but by alphabetical order, where uppercase immediatly follows lowercase (ie Hi There is sorted to { , e, e, h, H, i, r, T}).

This works using patterns:

//.{a___,b_,b_,c___}:>{a,b,c,b}
    a___       c___              (Three _) a zero or more members, named a and c
         b_,b_                   exactly one member, repeated twice (since they have the same name)
                    :>           Delayed Rule (replace left hand side with right hand side.)
                                 Delayed Rule evaluate on each substitution, avoiding conflicts with predefined variables
                      {a,b,c,b}  put one of the b-named member after all other sequences
//.                              repeat until no change (aka Replace Repeated)

spacemit

Posted 2017-01-10T21:19:54.400

Reputation: 41

1One minor thing: Rule (->) should be RuleDelayed (:>) (no change in byte count) because both sides of Rule has variables. Rule can cause conflicts with pre-existing definitions. For instance: a=3;5/.{a_->a} returns 3, not 5. (a_->a evaluates to a_->3 -- if you use a_:>a, it stays that way and a=3;5/.{a_:>a} returns 5). – JungHwan Min – 2017-01-13T01:33:02.140

I marked your answer non-competing because it does not do what the question specifies (sort by character code, not in canonical order). – JungHwan Min – 2017-01-13T01:56:39.380

@JungHwanMin fixed to RuleDelayed. thanks. – spacemit – 2017-01-17T08:17:16.253

2

Brainf*ck, 458 226 bytes

,[>>>>>>,]<<<<<<[[-<<<+<<<]>>>[>>>[>>>>>>]<<<[>>--[<->--]<-<[>->+<[>]>[<+>-]<<[<]>-]>>.[[-]<]<<<[[>>>>>>+<<<<<<-]<<<]>>>>>>]>>>[>>>[>>>>>>]<<<[>>--[<->--]<-<[>->+<[>]>[<+>-]<<[<]>-]>>[-<+<+>>]<[->>+<<]<[<<<<<<]>>>]>>>]]<<<<<<]

Try it online! - BF

Numberwang, 262 226 bytes

8400000087111111442111911170004000400000071114002241202271214020914070419027114170270034427171114400000091111112711170000007000400040000007111400224120227121402091407041902711417027004219190071420091171411111170007000771111117

Try it online! - NW

I put both of these here because they are identical code.

JungHwan Min

Posted 2017-01-10T21:19:54.400

Reputation: 13 290

2

PHP, 83 bytes

for($s=count_chars($argv[1]);$s=array_filter($s);$c%=128)echo$s[++$c]--?chr($c):'';

Unfortunately you can't have unset in a ternary so I need to use the annoyingly long array_filter.
Use like:

php -r "for($s=count_chars($argv[1]);$s=array_filter($s);$c%=128)echo$s[++$c]--?chr($c):'';" "If you sort a string you'll typically get something like:"

user59178

Posted 2017-01-10T21:19:54.400

Reputation: 1 007

2

V, 37 36 bytes

Thanks @DJMcMayhem for the byte!

Í./&ò
dd:sor
Íî
òͨ.©¨±«©±À!¨.«©/±³²

Try it online!

Not sure I like the regex at the end, but I needed to make the ò break somehow.

Explain

Í./&ò                    #All chars on their own line
dd:sor                   #Delete empty line, sort chars
Íî                       #Join all lines together s/\n//
òͨ.©¨±«©±À!¨.«©/±³² #until breaking s/\v(.)(\1+)\1@!(.+)/\3\2\1

nmjcman101

Posted 2017-01-10T21:19:54.400

Reputation: 3 274

Íî (or :%s/\n//g) is shorter than VGgJ – James – 2017-01-12T15:40:41.560

2

Python 2, 70 bytes

f=lambda s,i=0,c='':s[i>>7:]and(s.count(c)>i>>7)*c+f(s,i+1,chr(i%128))

Try it online

This is very inefficient. The test link changes the i>>7 to i>>5 and sets the recursion limit to 10000. Assumes the inputs only has ASCII values up to 126.

Uses the div-mod trick to iterate through two loops: minimum counts i/128 in the outer loop and ASCII values i%128 in the inner loop. Includes a character c with the given ASCII value if the number of times it appears in the string is at least its minimum count.

The code uses a trick to simulate the assignment c=chr(i%128) so that it can be referenced in the expression (s.count(c)>i>>7)*c. Python lambdas do not allow assignment because they only take expressions. Converting to a def or full program is still a net loss here.

Instead, the function pushes forward the value chr(i%128) to the next recursive call as an optional input. This is off by one because i has been incremented, but doesn't matter as long as the string doesn't contain special character '\x7f' (we could also raise 128 to 256). The initial c='' is harmless.

xnor

Posted 2017-01-10T21:19:54.400

Reputation: 115 687

1

Perl 6, 68 bytes

{my \a=.comb.sort;[~] flat roundrobin |a.squish.map({grep *eq$_,a})}

I was a little surprised to find that there's no built-in way to group like elements in a list. That's what the squish-map bit does.

Sean

Posted 2017-01-10T21:19:54.400

Reputation: 4 136

1I get "This Seq has already been iterated" unless I rename a to @a (+2 bytes). Also, grep *eq$_, can be written grep $_, (-3 bytes) since a string is a valid smart-matcher. – smls – 2017-01-11T14:25:58.253

1{[~] flat roundrobin |.comb.classify(~*){*}.sort»[*]} -- This variation is only 54 bytes. – smls – 2017-01-11T14:45:18.560

@smis I don't see that error. Maybe we're using different versions? I'm on rakudo-star-2016.10. Anyway, your solution puts mine to shame, you should post it as a separate answer. – Sean – 2017-01-11T17:33:19.420

I'm using a bleeding-edge Rakudo compiled from the main branch of the git repo this week. Anyway, I posted the classify-based solution as a separate answer now. – smls – 2017-01-11T18:43:20.690

1

JavaScript (ES6), 77 75 bytes

s=>(a=[],x={},[...s].sort().map(c=>a[x[c]=n=-~x[c]]=(a[n]||'')+c),a).join``

Stable sorts the lexicographically sorted string by nth occurence

F=s=>(a=[],x={},[...s].sort().map(c=>a[x[c]=n=-~x[c]]=(a[n]||'')+c),a).join``

const update = () => {
  console.clear();
  console.log(F(input.value));
};
input.oninput = update;
update();
#input {
  width: 100%;
  box-sizing: border-box;
}
<input id="input" type="text" value="         ':Iaaceeefggghiiiiklllllmnnooooprrssstttttuuyyyy" length=99/>
<div id="output"></div>

George Reith

Posted 2017-01-10T21:19:54.400

Reputation: 2 424

1+~~ is the same as -~. – Neil – 2017-01-11T09:34:42.290

@Neil Awesome thanks -2 bytes – George Reith – 2017-01-11T11:49:52.577

1

FSharp, 194 190 170 140 133 bytes

let f=Seq.map
let(@)=(>>)
f int@Seq.groupBy id@f(snd@Seq.mapi((*)128@(+)))@Seq.concat@Seq.sort@f((%)@(|>)128@byte)@Array.ofSeq@f char

Using Seq instead of Array saves a couple of bytes

Defining a shorter name, and using another maps to avoid a (fun ->) block

It turns out F# can map a char to an in, so removing the shortened name of System.Text.Encoding.ASCII, and adding in another map saves me 20 bytes!

Returning a char array instead of a string, saves me 30 bytes!

I no longer need to make sure it's a string, saves me 7 bytes

Cyclic3

Posted 2017-01-10T21:19:54.400

Reputation: 121

1

Perl 6, 54 bytes

{[~] flat roundrobin |.comb.classify(~*){*}.sort»[*]}

Explanation:

  • { }: A lambda that takes one argument -- e.g. 21211.
  • .comb: Split the input argument into a list of characters -- e.g. (2,1,2,1,1).
  • .classify(~*): Group the characters using string comparison as the grouping condition, returning an unordered Hash -- e.g. { 2=>[2,2], 1=>[1,1,1] }.
  • {*}: Return a list of all values of the Hash -- e.g. [2,2], [1,1,1].
  • .sort: Sort it -- e.g. [1,1,1], [2,2].
  • »[*]: Strip the item containers the arrays were wrapped in due to being in the hash, so that they won't be considered as a single item in the following step -- e.g. (1,1,1), (2,2).
  • roundrobin |: Zip the sub-lists until all are exhausted -- e.g. (1,2), (1,2), (1).
  • flat: Flatten the result -- e.g. 1, 2, 1, 2, 1.
  • [~]: Concatenate it to get a string again -- e.g. 12121.

(Credit for the roundrobin approach goes to Sean's answer.)

smls

Posted 2017-01-10T21:19:54.400

Reputation: 4 352

1

05AB1E, 15 bytes

{.¡"ä"©¹g׫øJ®K

Try it online! or as a Test suite

Explanation

{                # sort input
 .¡              # group by equal elements
   "ä"©          # push "ä" and store a copy in the register
       ¹g×       # repeat the "ä" input-nr times
          «      # concatenate the result to each string in the grouped input
           ø     # zip
            J    # join to string
             ®K  # remove all instances of "ä" in the string

10 of the 15 bytes are for getting around 05AB1E's way of handling zipping strings of different length.

Emigna

Posted 2017-01-10T21:19:54.400

Reputation: 50 798

0

JavaScript (ES6), 114 bytes

Separated with newline for clarity, not part of byte count:

s=>[...s].map(a=>(m[a]=-~m[a])*128+a.charCodeAt(),m={})
.sort((a,b)=>a-b).map(a=>String.fromCharCode(a%128)).join``

Demo

`**Test cases:**
 *:Tacest*es*s*

If you sort a string you'll typically get something like:
 ':Iacefghiklmnoprstuy aegilnorstuy egilosty iloty lt    

Hello, World!
 !,HWdelorlol

#MATLAB, 114 bytes
 #,14ABLMTbesty 1A

f=@(s)[mod(sort(cell2mat(cellfun(@(c)c+128*(0:nnz(c)-1),mat2cell(sort(s),1,histc(s,unique(s))),'un',0))),128),''];
'()*+,-0128:;=@[]acdefhilmnoqrstuz'(),0128@acefilmnorstu'(),12celmnostu'(),12celnstu(),clnst(),cls(),cs(),()()()()`.split`\n\n`.map(s=>(p=s.split`\n`,console.log(`${p[0]}\n\n${r=f(p[0])}\n\nmatch: ${r==p[1]}`)),
f=s=>[...s].map(a=>(m[a]=-~m[a])*128+a.charCodeAt(),m={}).sort((a,b)=>a-b).map(a=>String.fromCharCode(a%128)).join``)

Patrick Roberts

Posted 2017-01-10T21:19:54.400

Reputation: 2 475

The same bytecount as my Matlab code, and the exact same approach. Haven't attempted to golf mine yet though. I'll probably upvote later if you add an explanation :-) (I've made a principle out of not upvoting answers without explanations, even when I understand it) :-) – Stewie Griffin – 2017-01-10T23:51:44.243

0

Clojure, 79 bytes

#(for[V[(group-by(fn[s]s)%)]i(range 1e9)k(sort(keys V))c[(get(V k)i)]:when c]c)

An anonymous function, returns a sequence of characters. Supports up-to 10^9 repetitions of any characters, which should be plenty.

NikoNyrh

Posted 2017-01-10T21:19:54.400

Reputation: 2 361

0

Retina, 24 bytes

O`.
O$#`(.)(?=(\1)*)
$#2

Try it online!

Martin Ender

Posted 2017-01-10T21:19:54.400

Reputation: 184 808

0

Ruby, 59+1 = 60 bytes

Adds one byte for the -n flag. Port of @PatrickRoberts' dictionary solution.

d={};print *$_.chars.sort_by{|c|d[c]||=0;c.ord+128*d[c]+=1}

Value Ink

Posted 2017-01-10T21:19:54.400

Reputation: 10 608