14
\$\begingroup\$

This challenge is to take an alphabetical string as input and to apply the following conversion:

The first of each type of character of the string must stay, and must be immediately followed by an integer representing how many of these characters were in the original string. Any repeating characters must be omitted.

All inputs will be entirely lower-case letters (no spaces). Outputs must be ordered in the same way as the inputs (input hi must give output of h1i1, not i1h1)

Examples

Input: potato Output: p1o2t2a1

Input: pqwertyuiop Output: p2q1w1e1r1t1y1u1i1o1

Input: thisisanexample Output: t1h1i2s2a2n1e2x1m1p1l1

Input: oreganoesque Output: o2r1e3g1a1n1s1q1u1

Input: aaaaaaabaaaaaa Output: a13b1

Scoring

This is . Shortest answer wins!

\$\endgroup\$
  • \$\begingroup\$ Since the input cannot have spaces, can we separate the letters and the digits with spaces? \$\endgroup\$ – Adám Jun 9 '20 at 13:52
  • 4
    \$\begingroup\$ Suggested test case: abaaacaaaaaaa (more than 9 of the same letter). \$\endgroup\$ – Zgarb Jun 9 '20 at 14:45
  • 1
    \$\begingroup\$ @pppery I guess this is similar, but this question doesn't allow for duplicate characters at all. This question was really a test to see how fast challenges would be solved - my next challenge (in a few days) will be more creative and harder to solve \$\endgroup\$ – Daniel H. Jun 9 '20 at 16:41
  • 5
    \$\begingroup\$ @pppery I figured. This is my first challenge I've posted here after years of lurking (so I wanted to make something relatively simple to see how many answers I would get), I'm gonna try to make some really good challenges soon \$\endgroup\$ – Daniel H. Jun 9 '20 at 16:44
  • 8
    \$\begingroup\$ @pppery I don't think it's a duplicate, although it's close. That challenge asks to read all lines and to output the run-length encoding, whereas this one has a single input and outputs the uniquified run-length encoding (i.e. test would result in t1e1s1t1 in that other challenge, but t2e1s1 here). They're definitely closely related, but not a dupe imo. \$\endgroup\$ – Kevin Cruijssen Jun 9 '20 at 17:30

35 Answers 35

10
\$\begingroup\$

APL (Dyalog Unicode), 7 bytes (SBCS)

Anonymous tacit prefix function.

,,∘⍕∘≢⌸

Try it online!

 apply the following function between each unique character and the indices where it occurs:

, concatenate the character

   to

    the stringification

     of

      the indices' count

, flatten

\$\endgroup\$
5
\$\begingroup\$

PHP, 63 bytes

foreach(array_count_values(str_split($argn))as$a=>$b)echo$a.$b;

Try it online!

Built-ins and a whole lot of glue. Input via STDIN, output to STDOUT.

\$\endgroup\$
  • 1
    \$\begingroup\$ too bad count_chars doesn't return an array ordered as the string \$\endgroup\$ – Kaddath Jun 10 '20 at 7:45
  • 1
    \$\begingroup\$ A more interesting version is array_walk(array_count_values(str_split($argn)),fn($a,$b)=>print($b.$a));. Sadly, it's 72 bytes long... :( \$\endgroup\$ – Ismael Miguel Jun 12 '20 at 10:11
  • \$\begingroup\$ @IsmaelMiguel why would it be more interesting? it's basically the same with a callback overhead, longer, less performant.. just my opinion ;) A little advice, print doesn't need brackets: array_walk(array_count_values(str_split($argn)),fn($a,$b)=>print$b.$a); would do \$\endgroup\$ – Kaddath Jun 12 '20 at 12:12
  • \$\begingroup\$ @IsmaelMiguel I like the functional concept of that approach and it's too bad you can't do some variant like array_walk(array_count_values(str_split($argn)),print); (55 bytes) but that won't work sadly. \$\endgroup\$ – 640KB Jun 12 '20 at 12:26
  • \$\begingroup\$ @Kaddath Yes, a plain boring old loop sure is more interesting than implementing new concepts added in the latest versions of PHP .... (Yes, it's faster, but ...) \$\endgroup\$ – Ismael Miguel Jun 12 '20 at 14:15
5
\$\begingroup\$

Python 3, 58 \$\cdots\$ 50 57 bytes

Added 3 bytes and switched to Python 3 to fix bugs.
Added 7 bytes to make output a string.

lambda s:''.join(c+f'{s.count(c)}'for c in{}.fromkeys(s))

Try it online!

Outputs a string.

Outputting a list of strings is 50 bytes.

\$\endgroup\$
  • 1
    \$\begingroup\$ Using a dict is a really nice idea! It can be 2 bytes shorter with dict(zip(s,s)). \$\endgroup\$ – ovs Jun 9 '20 at 14:45
  • 1
    \$\begingroup\$ @ovs Thanks! Just saved 3 bytes using {} instead of dict :-) \$\endgroup\$ – Noodle9 Jun 9 '20 at 14:48
4
\$\begingroup\$

Wolfram Language (Mathematica), 34 bytes

This is the dreaded obvious chain-the-built-ins. Print can accept multiple arguments and will print them without any separators.

Print@@Flatten@Tally@Characters@#&

More scary version, for the same number of bytes:

Print@@(##&)@@@Tally@Characters@#&

I suspect that a better hack consisting entirely of the characters #&/() can be used to get rid of Flatten, but I couldn't come up with one.

Of course, Mathematica has both LetterCounts and CharacterCounts, and there's also Counts instead of Tally, but all of these seem to return association objects that seem excessively complicated to work with.

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Association objects can be converted to lists of Rules using Normal, but the Rule objects end up taking more bytes to work with. \$\endgroup\$ – LegionMammal978 Jun 11 '20 at 13:58
4
\$\begingroup\$

Python 3, 49 bytes

lambda s:''.join({c+str(s.count(c)):1for c in s})

Try it online!

Outputs a string.

Based on Noodle9's solution, which uses a nice idea of a dictionary to deduplicate while preserving order, which they do in Python 3 but not Python 2. Note that set doesn't preserve order.

The idea is to make the character-with-count strings be keys of a dictionary so that only the first instance is used. The values in the dictionary don't matter, since dictionaries iterate over keys by default.

I had thought at first that the deduplication must be applied to the characters of the input string, but realized that it also works on the strings to be joined in the output, since a given character is always attached to the same count.

Outputting a list of strings without joining takes 43 bytes.

\$\endgroup\$
4
\$\begingroup\$

K (oK), 23 18 bytes

{,/t,'$+/x=\:t:?x}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ ,/(!g),'$.g:#:'= for 16 bytes \$\endgroup\$ – streetster Jun 12 '20 at 13:17
  • \$\begingroup\$ @streetster In which verson of K? I can't make it work in oK in TIO \$\endgroup\$ – Galen Ivanov Jun 12 '20 at 13:31
  • \$\begingroup\$ oK - tio.run/##y9bNz/7/X0dfQzFdU0ddRS/dStlK3VapoLA8taiksjQzv0Dp/38A \$\endgroup\$ – streetster Jun 12 '20 at 13:31
  • 1
    \$\begingroup\$ Managed a 17 byte (function) with K4, f:{,/(?x),'.$#:'=x} but it works because the $ is applied to the value of the dictionary, not the whole thing. \$\endgroup\$ – streetster Jun 12 '20 at 13:36
  • 1
    \$\begingroup\$ @streetster I always define my answers as functions. Anyway, thank you for your solutions - I need to lear how to work with dictionaries. \$\endgroup\$ – Galen Ivanov Jun 12 '20 at 13:36
4
\$\begingroup\$

J, 15 17 bytes

Thanks to Jonah for finding and fixing a bug!

[:;~.<@,&":"0#/.~

Try it online!

\$\endgroup\$
  • 2
    \$\begingroup\$ ~.,@,.":@#/.~ is equivalent for 13 bytes, but both yours and the 13 byte version fail the final test case. This rather verbose 19 byte version passes all test cases. \$\endgroup\$ – Jonah Jun 9 '20 at 21:43
  • 2
    \$\begingroup\$ 17 bytes passing all test cases \$\endgroup\$ – Jonah Jun 9 '20 at 21:59
  • 1
    \$\begingroup\$ @Jonah Thanks! I think the last test case was added later and I didn't revisit my J solution. \$\endgroup\$ – Galen Ivanov Jun 10 '20 at 6:09
3
\$\begingroup\$

05AB1E, 6 bytes

Ùε¢yì?

Try it online or verify all test cases.

Explanation:

Ù       # Uniquify the (implicit) input-string
 ε      # For-each over each character in this string:
  ¢     #  Count the amount of times the current character occurs in the (implicit) input
   yì   #  Prepend the current character before this count
     ?  #  Print it without newline
\$\endgroup\$
3
\$\begingroup\$

Jelly, 5 bytes

Lƙż@Q

A full program printing the result (or a monadic Link yielding a list of pairs of characters and integers).

Try it online!

There are loads of ways to achieve this in 6 bytes (e.g. Qżċ@€¥).

How?

Lƙż@Q - Main Link: list of characters, S          e.g. "cabbage"
 ƙ    - for groups of identical elements (of S):       (c aa bb g e)
L     -   length                                       [1,2,2,1,1]
    Q - de-duplicate S                                 "cabge"
   @  - with swapped arguments:
  ż   -   zip                                          [['c',1],['a',2],['b',2],['g',1],['e',1]]
      - implicit, smashing, print                      c1a2b2g1e1
\$\endgroup\$
2
\$\begingroup\$

Python 3, 62 bytes

f=lambda s:s and s[0]+str(s.count(s[0]))+f(s.replace(s[0],''))

Try it online!

\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6), 55 bytes

s=>[...new Set(s)].map(c=>c+~-s.split(c).length).join``

Try it online!

Commented

s =>                    // s = input string
  [...new Set(s)]       // create a set from the input string and split it,
                        // resulting in an array of characters arranged in
                        // order of first appearance
  .map(c =>             // for each character c in this array:
    c +                 //   append c
    ~-s.split(c).length //   followed by the number of occurrences in the
                        //   original string
  )                     // end of map()
  .join``               // join everything
\$\endgroup\$
2
\$\begingroup\$

perl -p, 52 50 bytes

s/./$&1/g;1while s/(\D)\K(\d+)(.*)\1\d/($2+1).$3/e

Try it online!

Starts off by adding 1 to each character. Then, as often as possible, find a letter followed by a number, with the same letter elsewhere in the string followed by a digit (which has to be 1). Increment the count, and remove the same letters followed by its 1.

Initial solution, following a very different technique:

perl -nF//, 52 bytes

$F{$_}++for@F;$F{$_}&&print$_,$F{$_}xor$F{$_}=0for@F

Try it online!

Reads a line from STDIN, assuming it's not newline terminated.

Splits the input into characters, available (in order), in @F (due to the -F//). Counts the occurrence of each character in the hash %F. Then loops over @F: if present in %F with a true value, print the character and its count, then set the corresponding entry in %F to 0. This ensures each character is only outputted once.

The TIO code has some header and footer code so we can handle multiple test inputs. They're not needed if we just have one line of input.

\$\endgroup\$
2
\$\begingroup\$

Brachylog, 5 bytes

ọ∋∋w⊥

Try it online!

Full program, or alternatively a predicate which prints the output then fails. Could be one byte shorter if it could generate the characters as a mix of strings and integers, but that seems like a bit too far out there of an output format.

   w     Print (without a newline)
  ∋      an element of
 ∋       an element of
ọ        a list of pairs [element, # of occurrences] in order of first appearance,
    ⊥    then try it again some other way.
\$\endgroup\$
2
\$\begingroup\$

C (gcc), 105 \$\cdots\$93 90 bytes

Saved 2 5 bytes from the man himself Arnauld!!!

d;c;f(char*s){for(;d=1,c=*s;c-1&&printf("%c%d",c,d))for(char*q=s++;*++q;d+=*q==c?*q=1:0);}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ 93 bytes \$\endgroup\$ – Arnauld Jun 9 '20 at 18:10
  • \$\begingroup\$ @Arnauld Diabolical, was trying to align j and d and clean up the string access with no joy - thanks! :D \$\endgroup\$ – Noodle9 Jun 9 '20 at 18:51
  • \$\begingroup\$ Using a 2nd pointer is actually shorter: 90 bytes \$\endgroup\$ – Arnauld Jun 9 '20 at 22:21
  • \$\begingroup\$ @Arnauld Interesting, the thought crossed my mind but dismissed it right away - thanks! :-) \$\endgroup\$ – Noodle9 Jun 10 '20 at 7:27
2
\$\begingroup\$

AWK + -F '', 65 + 4 = 69 bytes

{while(i++<NF)if(!a[$i]++)b[i]=$i;while(j++<i)printf b[j]a[b[j]]}

Try it at awk.js.org

A shorter 64 59+4 byte program, which runs on GNU awk with -F '', is this:

{while(i++<NF)if(!a[$i]++)b[i]=1;for(i in b)printf$i a[$i]}

Annoyingly, though, while 'Try it online' links to the GNU awk manual page, it doesn't seem to use GNU awk, and refuses the -F '' command-line option.

The alternative link above (to awk .js.org) accepts the command-line option, but then outputs in a different order, which costs a frustrating additional one six bytes to correct (which I have included above as the price of verifiability).

\$\endgroup\$
  • \$\begingroup\$ we don't count flags as part of byte count any longer; instead, the flags used count as part of a separate language as opposed to just AWK. \$\endgroup\$ – Giuseppe Jun 10 '20 at 23:20
2
\$\begingroup\$

CJam, 12 bytes

Port of the Pyth answer.

q:A_&{_Ae=}%

Try it online!

Explanation

q            Take the whole input
 :A          Assign to a variable
   _&        Set union w/ itself
     {    }% Map:
      _          Join the uniquified character
       Ae=       With the count of the character in the input string.

CJam, 16 bytes

CJam has the built-in, so I guess it simplifies the question. Unfortunately the built-in does it in the wrong order...

q:A{A#}$e`{-1%}%

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Perl 5 -p, 28 bytes

s|.|($b=s/$&//g)?$&.$b:''|ge

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Nice one! Nested s/// feels like it shouldn't work! \$\endgroup\$ – Dom Hastings Jun 11 '20 at 6:20
  • 1
    \$\begingroup\$ @DomHastings I don't disagree. However, if it's stupid but works... :) \$\endgroup\$ – Xcali Jun 11 '20 at 17:43
2
\$\begingroup\$

K4 17 16 bytes

Solution:

{,/(?x),'$#:'=x}

Examples:

q)k){,/(?x),'$#:'=x}"potato"
"p1o2t2a1"
q)k){,/(?x),'$#:'=x}"pqwertyuiop"
"p2q1w1e1r1t1y1u1i1o1"
q)k){,/(?x),'$#:'=x}"thisisanexample"
"t1h1i2s2a2n1e2x1m1p1l1"
q)k){,/(?x),'$#:'=x}"oreganoesque"
"o2r1e3g1a1n1s1q1u1"
q)k){,/(?x),'$#:'=x}"aaaaaaabaaaaaa"
"a13b1"

Explanation:

{,/(?x),'$#:'=x} / the solution
{              } / lambda function taking implicit 'x' argument
             =x  / group x (dict of unique chars => indices)
          #:'    / count length of each group
         $       / cast to string
       ,'        / join each-both
   (  )          / do this together
    ?x           / distinct x
 ,/              / flatten
\$\endgroup\$
2
\$\begingroup\$

R, 72 71 bytes

cat(rbind(z<-unique(y<-el(strsplit(scan(,""),""))),table(y)[z]),sep="")

Try it online!

\$\endgroup\$
1
\$\begingroup\$

SNOBOL4 (CSNOBOL4), 96 bytes

	I =INPUT
N	I LEN(1) . X	:F(O)
	N =
S	I X =	:F(B)
	N =N + 1	:(S)
B	O =O X N	:(N)
O	OUTPUT =O
END

Try it online!

	I =INPUT		;* Read input, set to I
N	I LEN(1) . X	:F(O)	;* Get the first character of I as X; if I is empty then goto O
	N =			;* set N to empty string (evaled as 0 in arithmetic)
S	I X =	:F(B)		;* remove the first occurrence of X from I. If none exist, goto B
	N =N + 1	:(S)	;* increment N and goto S
B	O =O X N	:(N)	;* Add to the output string and goto N to get the Next character
O	OUTPUT =O		;* print the result
END
\$\endgroup\$
1
\$\begingroup\$

T-SQL, 111 bytes

Added a line change to make it readable

WHILE @+@ like'_[a-z]%'
SELECT @=concat(s,left(@,1),len(@)-len(s))FROM(SELECT
replace(@,left(@,1),'')s)s
PRINT @

Try it online

\$\endgroup\$
  • \$\begingroup\$ I'm one to talk posting golfed C, but, how's that readable? T_T \$\endgroup\$ – Noodle9 Jun 9 '20 at 17:41
  • \$\begingroup\$ @Noodle9 reading sql isn't that hard. The commands are simple and have nice long descriptive names - ill suited for golfing. But what I mean by readable, is that you don't have to scroll in order to see the whole text. \$\endgroup\$ – t-clausen.dk Jun 9 '20 at 18:02
1
\$\begingroup\$

JavaScript (V8), 106 102 bytes

e=>{for(o="",i=0;i<e.length;i++)o.includes(e[i])||(o+=e[i]+e.match(RegExp(e[i],"g")).length);return o}

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Pyth, 8 7 bytes

-1 byte thanks to @isaacg

s+R/Qd{

Try it online!

s+R/Qd{
      {  Deduplicate: keep the first occurrence of each unique character
  R      For each of these unique characters:
 +        - append the character
   /Qd    - to its count in the original input
s        Join the resulting strings
\$\endgroup\$
  • \$\begingroup\$ m+d is equivalent to +R \$\endgroup\$ – isaacg Jun 9 '20 at 15:17
  • \$\begingroup\$ @isaacg Thanks! I didn't realize that R and d could be used together \$\endgroup\$ – math junkie Jun 9 '20 at 15:19
1
\$\begingroup\$

Stax, 7 bytes

ô!Ω;òá☺

Run and debug it

\$\endgroup\$
1
\$\begingroup\$

Retina 0.8.2, 31 bytes

+`(.)(.+)\1
$1$1$2
(.)\1*
$1$.&

Try it online! Link includes test cases, unusually without even needing a header. Explanation:

+`(.)(.+)\1
$1$1$2

Collect all repeated characters into a single run at the first appearance.

(.)\1*
$1$.&

Replace each run with its first character and its length.

\$\endgroup\$
1
\$\begingroup\$

Charcoal, 14 bytes

⭆Φθ¬№…θκι⁺ι№θι

Try it online! Link is to verbose version of code. Explanation:

  θ             Input string
 Φ              Filter over characters
    №           Count of
        ι       Current character in
      θ         Input string
     …          Truncated to length
       κ        Current index
   ¬            Is zero
⭆               Map over unique characters and join
          ι     Current character
         ⁺      Concatenated with
           №    Count of
             ι  Current character in
            θ   Input string
                Implicitly print
\$\endgroup\$
1
\$\begingroup\$

Kotlin, 69 67 bytes

fun String.f()=groupBy{it}.map{(a,b)->"$a"+b.size"}.joinToString("")

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Ruby 2.7 -paF|, 17 bytes

$_=[*$F.tally]*''

Try it online!

This is more or less built in to the recent Ruby release as the tally method.

\$\endgroup\$
1
\$\begingroup\$

C# (Visual C# Interactive Compiler), 57 bytes

s=>s.GroupBy(c=>c).Aggregate("",(r,g)=>r+g.Key+g.Count())

Try it online!

\$\endgroup\$
1
\$\begingroup\$

SimpleTemplate, 54 47 bytes

This was a very easy, but incredibly fun challenge!

The code simply loops through every character and counts how many times it shows, and presents it all again.
Nothing fancy at all...

{@eachargv.0}{@incR.[_]}{@/}{@eachR}{@echo__,_}

Hey, I didn't say the code was readable!


Here's an ungolfed and readable version:

{@each argv.0 as char}
    {@inc by 1 result.[char]}
{@/}
{@each result as times key char}
    {@echo char, times}
{@/}

Should be easy to understand ...
{@inc} increments the value or creates a new one, if it doesn't exist. (this keeps key ordering).


You can try this on http://sandbox.onlinephpfunctions.com/code/a180782e659c29674fbb0d77dc82d90d238c6e08
Older version: http://sandbox.onlinephpfunctions.com/code/6ee5077eaf38ec445d84086cc07966026ca7c565

(There, you have an example on how to use this in a function, with multiple tests.)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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