16
\$\begingroup\$

Shorten (or not) text using run length encoding

Input:

heeeello
woooorld

Output:

1h4e2l1o
1w4o1r1l1d
  • Read lines from stdin.
  • Print to stdout.
  • Stderr is of course discarded.
  • Assume there are hidden testcases (no embedding of the output)
  • Input/Output as ASCII
  • Any language is accepted
\$\endgroup\$
10
  • 4
    \$\begingroup\$ You can (typically) save quite a bit if you ignore all ones, e.g. w4orld instead of 1w4o1r1l1d (you'd need to escape numerics, e.g. `f111 -> f3\1´). But then it would be a near-duplicate of this: codegolf.stackexchange.com/questions/6774 \$\endgroup\$
    – primo
    Sep 14, 2012 at 9:54
  • 1
    \$\begingroup\$ As it is it's close enough to Run-Length Encoding that I vote to close as dupe. It's not going to provide any new challenge or points of interest. \$\endgroup\$ Sep 14, 2012 at 12:15
  • 5
    \$\begingroup\$ @FUZxxl, 22 is a trivial fixpoint. \$\endgroup\$ Sep 17, 2012 at 18:15
  • 2
    \$\begingroup\$ @PeterTaylor And the only nonempty one. We know it must begin with a digit. 11 is impossible. 22 must end there or be followed by another fixed point not beginning with 2. 333nnn is an impossible pattern, for you'll never find the same character at consecutive odd indices. 4444 and up fail for the same reason. \$\endgroup\$ Aug 5, 2019 at 21:26
  • 1
    \$\begingroup\$ DIfferent input and output formats are not enough to render questions distinct. \$\endgroup\$ Aug 8, 2019 at 3:27

28 Answers 28

4
\$\begingroup\$

Perl: 46 → 36 or 27 characters

perl -pe's|((.)\2*)|@x=split//,$1;@x.$x[0]|eg'

All hail @ardnew for coming up with the idea of using the tr///c operator to count the number of characters in the matched string instead of splitting:

perl -pe's|((.)\2*)|$1=~y///c.$2|eg'

Degolfed:

while(defined($_ = <>)) {
  $_ =~ s{((.)\2*)}           # match 1 or more consecutive identical non-newlines
         {
           ($1 =~ y///c )     # count the number of characters in $1
           .                  # and concatenate it
           $2                 # with the first matched character
         }eg;                 # execute substitution, match "global"
  print $_;                   # print the modified line
}

Usage:

$ perl -pe's|((.)\2*)|$1=~y///c.$2|eg' infile

or via STDIN

$ perl -pe's|((.)\2*)|$1=~y///c.$2|eg'
heeeello

prints

1h4e2l1o
\$\endgroup\$
3
  • \$\begingroup\$ You're short-changing yourself on your character count - I count 37 characters including 1 for the p option. \$\endgroup\$
    – Gareth
    Sep 14, 2012 at 22:30
  • \$\begingroup\$ You can save 10 chars by using s|((.)\2*)|$1=~y///c.$2|eg, which sums to 27 total chars (using the same character counting rules as @Gareth) \$\endgroup\$
    – ardnew
    Sep 15, 2012 at 3:29
  • 3
    \$\begingroup\$ Can shorten even further to 25 bytes (including -p) by eliminating the outer parens: Try it online! \$\endgroup\$
    – Xcali
    Aug 5, 2019 at 22:02
3
\$\begingroup\$

K (oK), 28 bytes

{,/($#:'c),'*:'c:(&~=':x)_x}

Try it online!

On mobile, explanation to follow...

\$\endgroup\$
1
  • \$\begingroup\$ I think three bytes can be saved with {($#x),*x}' instead of ($#:'c),'*:'c: \$\endgroup\$
    – coltim
    Mar 18, 2021 at 21:18
3
\$\begingroup\$

Stax, 7 bytes

ûèB☼å°╤

Run and debug it st staxlang.xyz!

Unpacked (8 bytes) and explanation:

m|RFNppz
m           For each line of input:
 |R           Run-length encode: "heeeello" -> [[104,1],[101,4],[108,2],[111,1]]
   F          For each pair:
    N           Uncons-left: [104,1] -> push [104]; push 1
     ppz        Pop and print. Pop and print. Push "".
              Implicit print (always an empty string) with a newline

5 bytes, works only on a single line:

∩l↨me
|RmEp]    Unpacked
|R        Run-length encode: "heeeello" -> [[104,1],[101,4],[108,2],[111,1]]
  m       Map block over input:
   E        Explode array: [104,1] -> push 104, push 1
    p       Pop and print with no newline
     ]      Make a one-element list: 104 -> [104] (which is "h")
            Implicit print with newline

Run and debug it at staxlang.xyz!

Perhaps not legal. This program prints each pair on a line of its own. A bit sketchy.

If that output format is illegal, I give you 6 bytes:

╡δôZ→╬
|RFEp]p    Unpacked
  F        For each item in array, execute block:
      p      Pop and print with no newline
             No implicit print in for-each block, so no extra newlines

Run and debug it at staxlang.xyz!

The language's creator recursive points out that uncons-right (N) can shorten this to six bytes unpacked, as it handles the E and the ] on its own. Programs this short, however, often get no shorter when packed, and this is am example. Still six bytes: |RFNpp Edit: Had to update my main answer; this form is what I used.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ NIcely done. |RFNpp can give the specified output in 6 bytes unpacked, but unfortunately, doesn't pack. \$\endgroup\$
    – recursive
    Aug 6, 2019 at 3:17
  • 1
    \$\begingroup\$ @KevinCruijssen Yep. Whoops. \$\endgroup\$ Aug 7, 2019 at 10:59
3
\$\begingroup\$

Scala, 150 144 134 bytes

io.Source.stdin.getLines.map(s=>println{val(x,y,z)=s.tail./:(("",s.head,1)){case((a,b,c),d)=>if(b==d)(a,b,c+1)else(a+c+b,d,1)};x+z+y})

Try it online!

UPDATE:

  • 6 bytes saved thanks to @Kjetil S.
  • 10 bytes saved thanks to @user

Here the pure Lambda de-golfed (97 bytes):

  s => {
    val (x,y,z) = s.tail./:(("", s.head, 1)) {
      case ((a, b, c), d) =>
        if (b == d)
          (a, b, c + 1)
        else
          (a + c + b, d, 1)
    }
    x+z+y
  }
\$\endgroup\$
4
  • \$\begingroup\$ You could cut scala. before io. Also I think some surrrounding code can be removed from the count. Only include the lambda to get 110 bytes: Try it online! \$\endgroup\$
    – Kjetil S
    Oct 24, 2020 at 10:13
  • \$\begingroup\$ Thanks for pointing out that scala. is obsolete, which saves 6 bytes. Regarding the lambda: I agree that it is generally ok to provide just a lambda. However, this challenge mentions explicitly that the solution should read from stdin and print to stdout, and other solutions (e.g., Python) do also include this in their byte count, so I think it is fair to keep it. \$\endgroup\$ Oct 26, 2020 at 7:15
  • \$\begingroup\$ You can use map instead of foreach, and /: instead of foldLeft. \$\endgroup\$
    – user
    Mar 16, 2021 at 16:49
  • \$\begingroup\$ I really feel bad to use map for side-effects, but, hey, this is golfing, so it's a great tip :) Also I did not know about the /: alias, thanks! \$\endgroup\$ Mar 18, 2021 at 7:55
3
\$\begingroup\$

Add++, 74 bytes

D,g,@:,BGd€q$€bLzBFJ
D,f,@,10C$t€g
]getchar
y:''
Wx,`y,+x,`x,]getchar
$f>y

Try it online!

This could be half the size if we could take input from the command line

How it works

The first line (the function g) actually performs RLE. The rest of it just reads the input char by char, passes it to a function f, which splits on newlines, then runs g on each line.

g simply groups equal adjacent characters with BG, then gets the lengths (€bL) and deduplicates (€q). We then zip these together, flatten, Join and print.

\$\endgroup\$
1
  • \$\begingroup\$ Remind me to upvote this in 20m :p \$\endgroup\$ Apr 7, 2021 at 23:40
2
\$\begingroup\$

J, 35 31 characters

,(](":@#,{.);.1~1,2~:/\])1!:1[1

Usage:

   ,(](":@#,{.);.1~1,2~:/\])1!:1[1
heeeello
1h4e2l1o
   ,(](":@#,{.);.1~1,2~:/\])1!:1[1
woooorld
1w4o1r1l1d
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Using modern site rules and a function, [:,(#,&":{.)/.~ for 15: Try it online! \$\endgroup\$
    – Jonah
    Aug 7, 2019 at 23:32
2
\$\begingroup\$

05AB1E, 9 bytes

|εÅγs.ιJ,

Try it online.

Or alternatively:

|εÅγøí˜J,

Try it online.

Explanation:

|          # Read all lines of input as list
 ε         # For-each over the lines:
  Åγ       #  Run-length encode, pushing the list of characters and lengths separately
    s      #  Swap so the characters at at the top and lengths below it
     .ι    #  Interleave the two lists
       J   #  Join the list of characters and lengths together to a single string
        ,  #  And output it with trailing newline

|εÅγ       # Same as above
    ø      #  Zip/transpose; creating pairs of [character, length]
     í     #  Reverse each pair to [length, character]
      ˜    #  Deep flatten the pairs to a single list
       J,  #  Join them together to a single string, and output it with trailing newline
\$\endgroup\$
2
\$\begingroup\$

Ruby, 42 bytes

->s{s.gsub(/(.)\1*/){|m|"#{m.size}"+m[0]}}

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ Recently re-visited this and noticed you can replace m[0] with $1 for -2 and if you switch to ruby -p you can drop the ->s{s. ... } for another -7 :) \$\endgroup\$ Nov 6, 2023 at 20:16
2
\$\begingroup\$

Python 3, 84 bytes

def f(s,c=1):i,*j=s;b=j[:1]==[i];print(end='%s%s'%(c,i)*(b^1));f(j,1+b*c)
f(input())

Try it online!

Explanation

Checks if the first and second characters of the string are equal. If they are, increase the counter by 1. If they are not, print the counter and the first item and reset the counter to 1. In both cases, the function is called recursively with the first character removed.

Raises an error when end of string is reached.


Without I/O restrictions, but with minimal byte count:

Python 3.8 (pre-release), 70 bytes

f=lambda s:'%s%s'%(len(s)-len(t:=s.lstrip(p:=s[0])),p)+f(t)if s else''

Try it online!

Python 3 equivalent (77 bytes)

Explanation

Strips all repeating characters off the start of the string. Then it returns a string containing (1.) the difference in lengths between the original string and the stripped string; (2.) the first character of the original string; (3.) the result of the recursive function applied to the stripped string. Recursion ends when an empty string is encountered.

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

Wolfram Language (Mathematica), 98 bytes

Print[""<>StringCases[#,s:x_..:>ToString@StringLength@s<>x]]&/@StringSplit[$ScriptInputString,"
"]

Try it online!

A more flexible I/O format reduces this solution to 54 bytes:

""<>StringCases[#,s:x_..:>ToString@StringLength@s<>x]&

Try it online!

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

CJam, 2 bytes

e`

e` is a built-in for run-length encoding. CJam's implicit output ignores array brackets, and so turns [[1 'h] [2 'e]] into "1h2e"

Try it online!

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

Husk, 10 bytes

m(ṁ§:osL←g

Try it online!

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

Haskell, 78 bytes

import Data.List
m@main=getLine>>=putStrLn.(>>=shows.length<*>take 1).group>>m

Try it online!

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

05AB1E, 5 bytes

Åγsø˜

Try it online!

Åγsø˜  # full program
    ˜  # flatten each sublist of...
   ø   # list of all elements of...
Åγ     # chars and corresponding run lengths of chars in...
       # implicit input...
   ø   # with each element from...
  s    # second...
   ø   # sublist paired with corresponding element in...
  s    # first...
   ø   # sublist
       # implicit output
\$\endgroup\$
2
\$\begingroup\$

JavaScript (V8), 66 bytes

-10 thanks to @Original Original Original VI

r=>r.split`
`.map(d=>d.replace(/(.)\1*/g,g=>g.length+g[0])).join`
`

My first time using \1 in a regular expression, which is a super overpowered way to approach this problem (looks like a few answers already use it). The rest of the code is basically just I/O.

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

Scala, 116 bytes

_ split "\n"map{def d:Seq[_]=>Any={case e+:f=>val(g,h)=f span e.==
""+e+(g.size+1)+d(h)case x=>x}
d(_)}mkString "\n"

Try it online!

Is it short? No. Does it use best practices where it wouldn't make the code longer? No. Did I do it for ethical reasons? No. Is it creative? No. Am I proud of it? Yes.

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

Brachylog, 11 bytes

ḅ⟨{lṫ}ch⟩ᵐc

Try it online!

(If output really has to be on stdout, add one byte for w at the end.)

          c    The output is the concatenation of
 ⟨    c ⟩ᵐ     the concatenated pairs of
  {lṫ}         length converted to a string
       h       and first element
ḅ        ᵐ     for every run in the input.
\$\endgroup\$
1
\$\begingroup\$

Python 3 iterative, 115 99 97 bytes

while 1:
 a=b='';k=0
 for c in input():e=a!=c;b+=(str(k)+a)*e;k+=1-k*e;a=c
 print(b[1:]+str(k)+a)

Try it online!

Python 3 recursive, 136 130 129 bytes

f=lambda r,c,s,k=1:s and(c==s[0]and f(r,c,s[1:],k+1)or f(r+str(k)+c,s[0],s[1:]))or r[1:]+str(k)+c
while 1:print(f('','',input()))

Try it online!

The iterative approach seems quite successful, while the recursive version has a lot of room for improvement.

\$\endgroup\$
3
  • \$\begingroup\$ Nice approach! It looks like the while loop in your code is only there to demonstrate the input. Without it, your code is still valid. In that case, the loop does not need to be part of the code and you can reduce the first example to 85 bytes like so: Try it online! \$\endgroup\$
    – Jitse
    Aug 7, 2019 at 9:09
  • \$\begingroup\$ Your second example can be reduced to 121 bytes like this: Try it online! \$\endgroup\$
    – Jitse
    Aug 7, 2019 at 9:12
  • \$\begingroup\$ Yea, the while loop is only for linewise input. But the question requires to read all lines, not just one, so externalising the loop would be against the rules. \$\endgroup\$
    – movatica
    Aug 7, 2019 at 17:12
1
\$\begingroup\$

Retina, 12 bytes

(.)\1*
$.0$1

Try it online.

Explanation:

Get a part of 1 or more of the same character, capturing the character in capture group 1.

(.)\1*

Replace it with the length of the total match, concatted with the character from capture group 1:

$.0$1
\$\endgroup\$
1
\$\begingroup\$

Julia 1.1, 94 84 81 bytes

foldl(((n,l),c)->(c==l||print(n,l);((c==l&&n)+1,c)),readline()*'\n',init=("",""))

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ Here is 80 bytes. I think it can go shorter though \$\endgroup\$
    – H.PWiz
    Aug 7, 2019 at 21:19
  • \$\begingroup\$ Unfortunately, your code didn't work on Julia 1.1 for me. I still managed to get 81 by adding an extra '\n' to readline() instead of printing the last tuple manually \$\endgroup\$ Aug 8, 2019 at 14:51
1
\$\begingroup\$

Vyxal, d, 2 bytes

øe

Try it Online!

That is,

øe  # run length encode the implicit input
    # the d flag deep sums the top of the stack before outputting
\$\endgroup\$
1
\$\begingroup\$

Pyth, 6 5 bytes

jksr8

Relatively self explanatory, just joins together Pyth's run length encode instruction

Edit: -1 byte because while working on something completely different I remembered flattening a 2D list is just summing the elements.

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Bash: 104 characters

while read s;do e=;while [[ $s ]];do c=${s:0:1};n=${s##+($c)};e+=$[${#s}-${#n}]$c;s=$n;done;echo $e;done

Sample run:

bash-4.2$ while read s;do e=;while [[ $s ]];do c=${s:0:1};n=${s##+($c)};e+=$[${#s}-${#n}]$c;s=$n;done;echo $e;done <<END
heeeello
woooorld
END
1h4e2l1o
1w4o1r1l1d
\$\endgroup\$
0
\$\begingroup\$

Zsh, 117

while read s;do n=1;for i in {1..$#s};do if [[ $s[i] != $s[i+1] ]];then echo -n $n$s[i];n=0;fi;((n++));done;echo;done

Run it like this:

zsh script.zsh < infile

De-golfed

while read s; do                      # while stdin has more
  n=1                                 # repeat counter
  for i in {1..$#s}; do               # for each character
    if [[ $s[i] != $s[i+1] ]]; then   # same as next one?
      echo -n $n$s[i]                 # print if no
      n=0
    fi
    ((n++))
  done
  echo                                # newline between words
done
\$\endgroup\$
2
  • \$\begingroup\$ Are those white spaces necessary or can you shorten `if [' to 'if[' etc? \$\endgroup\$
    – mroman
    Sep 14, 2012 at 11:19
  • \$\begingroup\$ The [[ construct is a command on it's own (like [) and has to be separated from other commands. As to using [ over [[, it requires the arguments to be quoted so four " need to be added. \$\endgroup\$
    – Thor
    Sep 14, 2012 at 12:00
0
\$\begingroup\$

APL (24)

,↑{(⍕⍴⍵),⊃⍵}¨B⊂⍨B≠¯1⌽B←⍞
\$\endgroup\$
0
\$\begingroup\$

Burlesque (17B)

{=[{J[-jL[Q}\m}WL

{=[{^^[~\/L[Sh}\m}WL

Older/Alternative and longer versions:

{=[{^^L[Sh\/-]Sh.+}m[\[}WL
{=[{^^L[Sh\/-][-.+}m[\[}WL
{=[{^^L[Sh\/-~.+}m[\[}WL
{=[{^^L[Sh\/-].+}\m}WL
{=[{^^L[Sh\/[~.+}\m}WL
{=[{^^L[Sh\/[~_+}\m}WL
{=[{^^L[Sh\/fc.+}\m}WL
{=[{^^L[Sh\/-~.+}\m}WL
{=[{^^L[Sh\/-]\/}\m}WL
\$\endgroup\$
0
\$\begingroup\$

rs, 19 chars

This doesn't really count because I created rs way after this was posted...but it was fun anyway!

(.)(\1*)/(^^\1\2)\1

Try it here!

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

Zsh, 44 bytes

44 bytes: try it online. Based on this.

echo ${(s::)1}|xargs -n1|uniq -c|tr -d \\n\ 

70 bytes: try it online. Based on this.

for ((n=1;i++<#w;n++))[[ $w[i] != $w[i+1] ]]&&printf $n$w[i]&&n=0
echo
\$\endgroup\$
2

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.