Shorten text with Run Length Encoding

Shorten (or not) text using run length encoding

Input:

heeeello
woooorld


Output:

1h4e2l1o
1w4o1r1l1d
• 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
• 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 – primo Sep 14 '12 at 9:54
• 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. – Peter Taylor Sep 14 '12 at 12:15
• @FUZxxl, 22 is a trivial fixpoint. – Peter Taylor Sep 17 '12 at 18:15
• @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. – Khuldraeseth na'Barya Aug 5 '19 at 21:26
• DIfferent input and output formats are not enough to render questions distinct. – pppery Aug 8 '19 at 3:27

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

• You're short-changing yourself on your character count - I count 37 characters including 1 for the p option. – Gareth Sep 14 '12 at 22:30
• 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) – ardnew Sep 15 '12 at 3:29
• Can shorten even further to 25 bytes (including -p) by eliminating the outer parens: Try it online! – Xcali Aug 5 '19 at 22:02

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.

• NIcely done. |RFNpp can give the specified output in 6 bytes unpacked, but unfortunately, doesn't pack. – recursive Aug 6 '19 at 3:17
• @KevinCruijssen Yep. Whoops. – Khuldraeseth na'Barya Aug 7 '19 at 10:59

Scala, 150144 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
}

• 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! – Kjetil S. Oct 24 '20 at 10:13
• 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. – cubic lettuce Oct 26 '20 at 7:15
• You can use map instead of foreach, and /: instead of foldLeft. – user Mar 16 at 16:49
• 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! – cubic lettuce Mar 18 at 7:55

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. • Remind me to upvote this in 20m :p – Redwolf Programs Apr 7 at 23:40 K (oK), 28 bytes {,/($#:'c),'*:'c:(&~=':x)_x}


Try it online!

On mobile, explanation to follow...

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

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


Ruby, 42 bytes

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


Try it online!

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.

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! 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! Husk, 10 bytes m(ṁ§:osL←g  Try it online! Haskell, 78 bytes import Data.List m@main=getLine>>=putStrLn.(>>=shows.length<*>take 1).group>>m  Try it online! 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  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. • @OriginalOriginalOriginalVI Oh, didn't notice that. Thanks! – Redwolf Programs Apr 5 at 19:37 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. 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  • Using modern site rules and a function, [:,(#,&":{.)/.~ for 15: Try it online! – Jonah Aug 7 '19 at 23:32 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.  Python 3 iterative, 11599 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, 136130 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. • 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! – Jitse Aug 7 '19 at 9:09 • Your second example can be reduced to 121 bytes like this: Try it online! – Jitse Aug 7 '19 at 9:12 • 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. – movatica Aug 7 '19 at 17:12 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  Julia 1.1, 9484 81 bytes foldl(((n,l),c)->(c==l||print(n,l);((c==l&&n)+1,c)),readline()*'\n',init=("",""))  Try it online! • Here is 80 bytes. I think it can go shorter though – H.PWiz Aug 7 '19 at 21:19 • 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 – Simeon Schaub Aug 8 '19 at 14:51 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  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! 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


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

• Are those white spaces necessary or can you shorten if [' to 'if[' etc? – mroman Sep 14 '12 at 11:19
• 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. – Thor Sep 14 '12 at 12:00

APL (24)

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


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


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!

Zsh, 70 bytes

try it online!

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


This is a much golfier version of the earlier zsh answer (tio link). Could probably be golfed more using string=>array conversion instead of iteration.