Run-Length Encoding

19

Write a program uses run-length encoding to shorten a list of non-negative integers it has to read in.

You can assume the non-negative integers can fit in 32bit signed integers.

Input Format

The length, n, of the list on the first line.

On the second line, a space-separated list of integers representing the list of integers.

Output Format

A space separated list of integers. The first 2 integers represent the first run, the next 2 integers the second run and so on. For each pair of integers representing a run, the first integer represents the length of the run and the second represents the value of the integer in the run.

Sample Input

1.

5
1 1 3 2 2

2.

3
1 1 1

Sample Output

1.

2 1 1 3 2 2

2.

3 1

Limits

0<n<10000

JPvdMerwe

Posted 2011-02-18T08:18:14.117

Reputation: 2 565

Question was closed 2019-08-08T14:39:24.747

Similar to golf.shinh.org/p.rb?Look+and+say and stackoverflow.com/questions/3908513/code-golf-morris-sequence – Nabb – 2011-02-18T10:08:02.120

I think you should give a more tricky input, like 36/1 1 1 1 1 1 3 3 3 3 3 2 2 7 7 7 7 4 4 9 9 9 9 9 9 9 9 9 4 4 8 3 3 3 3 0, giving 6 1 5 3 2 2 4 7 2 4 9 9 2 4 1 8 4 3 1 0, to ensure correct output when there are several distinct sequences of same number. – PhiLho – 2011-07-28T19:55:14.493

Answers

24

sh - 33 29 28

read;echo`xargs -n1|uniq -c`

Usage:

$ cat input|sh 1015.sh
2 1 1 3 2 2
  • read skips the first line

  • xargs -n1 reads the reast and outputs each number on one line:

    1
    1
    3
    2
    2
    
  • uniq -c filters adjacent matching lines (with the c switch it also prints the number of adjancent lines) :

    2 1
    1 3
    2 2
    
  • echo sees those numbers as separate arguments and just prints them separated by a space:

    2 1 1 3 2 2
    

Arnaud Le Blanc

Posted 2011-02-18T08:18:14.117

Reputation: 2 286

WTF? How is that possible? – FUZxxl – 2011-02-18T20:16:40.843

added explanations :-) – Arnaud Le Blanc – 2011-02-18T20:26:55.260

Very nice! (more chars) – J B – 2011-02-18T20:50:56.003

1You might be able to replace tail -n1| with a well-placed read; – J B – 2011-02-18T20:52:38.123

2And the tr can be replaced with an xargs. read;echo\xargs -n1|uniq -c`` for 28 characters. – J B – 2011-02-18T21:03:42.343

5

Perl, 46 56 68

$_=<>;s/(?{$a=1})(\d+)( \1(?{++$a}))*/$a $1/g

Run with the p command-line option (counted in code size):

$ perl -pe '$_=<>;s/(?{$a=1})(\d+)( \1(?{++$a}))*/$a $1/g'
5
1 1 3 2 2
  => 2 1 1 3 2 2
3
1 1 1
  => 3 1

J B

Posted 2011-02-18T08:18:14.117

Reputation: 9 638

3

Haskell (84 82)

import List
main=interact$unwords.(>>= \x->[show$length x,x!!0]).group.tail.words

Number of elements in the list is ignored.

FUZxxl

Posted 2011-02-18T08:18:14.117

Reputation: 9 656

3

Ruby - 57

$><<[*$<][-1].gsub(/(\d+)( \1)*/){"#{$&.split.size} "+$1}

Ungolfed:

length = STDIN.readline
input = STDIN.readline
print input.gsub(/(\d+)( \1)*/) { |match|
    "%d %s" % [ match.split.size, $1 ]
}

Arnaud Le Blanc

Posted 2011-02-18T08:18:14.117

Reputation: 2 286

1Fails on integers more than 2 digits wide. – J B – 2011-02-18T16:10:36.440

oops ! thanks, fixed now – Arnaud Le Blanc – 2011-02-18T16:17:02.143

3

Python - 92

Here's my attempt, which is mostly what I had before I posted this question, though for some reason I used a literal space instead of a comma.

from itertools import*
r=raw_input
r()
for k,g in groupby(r().split()):print len(list(g)),k,

JPvdMerwe

Posted 2011-02-18T08:18:14.117

Reputation: 2 565

3

Scala - 136

def f(s:Seq[_]):String=if(s.isEmpty)""else{val(l,r)=s.span(s.head==);l.size+" "+s.head+" "+f(r)}
readLine
println(f(readLine split' '))

Daniel C. Sobral

Posted 2011-02-18T08:18:14.117

Reputation: 253

Beautiful! I attempted a recursive solution, but it was longer than my attempt above. And I totally missed the span function, the key here. I also didn't know you can do s.head==, a devious trick! Note: according to my editor, that's 135 chars (with LF), and 133 if we don't count the newlines (as others did). – PhiLho – 2011-07-28T19:42:39.217

@PhiLho I did a wc on it. You probably don't have a LF on the last line, and I did. I think LF must be counted, because they have actual meaning here -- I'd have to use ; if I made it a one-liner. – Daniel C. Sobral – 2011-07-28T20:58:40.023

I agree we should count the LF as they are significant separators, but it looks like Python programs above doesn't count them, while they are as significant... You can get rid of the trailing LF for golf purpose (I too always have it!). And even use a plain 'print' as hamsterofdeath did. Note: I like your version because it is readable even in the golf state! – PhiLho – 2011-07-29T05:40:35.743

You can get this down to 126 by changing s.isEmpty -> s==Nil; s.head -> s(0); println -> print – Luigi Plinge – 2011-11-25T04:35:34.400

also first readLine -> readInt – Luigi Plinge – 2011-11-25T05:00:10.117

3

Scala - 101 characters

This is based on the regex solution. I didn't know this was valid regex in Java, actually! (Scala's regex is based on Java's)

print("(\\d+)( \\1)*".r.replaceAllIn({readLine;readLine},m=>(m.matched split' 'size)+" "+m.group(1)))

Daniel C. Sobral

Posted 2011-02-18T08:18:14.117

Reputation: 253

3

R, 33 bytes

Late answer....

cat(t(mapply(c,rle(scan()[-1]))))

Ungolfed

scan()[-1]      # read in from stdin [-1] drops first element
rle(..)         # perform run length encoding (return list with lengths and values)
mapply(c, list) # converts list to matrix
t()             # transposes this matrix so when extracted as a vector is length value ...
cat()           # writes to stdout (separated by space)

mnel

Posted 2011-02-18T08:18:14.117

Reputation: 826

2

Python - 106 chars

Very simple conceptually, but some very expensive words weigh it down

from itertools import*
input()
print" ".join(`len(list(j))`+' '+i for i,j in groupby(raw_input().split()))

gnibbler

Posted 2011-02-18T08:18:14.117

Reputation: 14 170

Wow. Haskell shorter than Python. That's nice. – FUZxxl – 2011-02-18T09:12:08.237

You don't consider the first line of input. – fR0DDY – 2011-02-18T09:16:25.243

@fR0DDY, ah well there goes another 8 strokes – gnibbler – 2011-02-18T09:26:14.740

2

C 101 Characters

#define p printf("%d %d ",c,l)
main(c,i,l){gets(&i);for(c=0;~scanf("%d",&i);l=i)i!=l&&c?p,c=1:c++;p;}

fR0DDY

Posted 2011-02-18T08:18:14.117

Reputation: 4 337

2

Python 100 Characters

c=l=0
input()
for i in raw_input().split():
 if i!=l and c:print c,l,;c=1
 else:c=c+1
 l=i
print c,l

fR0DDY

Posted 2011-02-18T08:18:14.117

Reputation: 4 337

2

Python - 110 chars

import re;R=raw_input;R()
print" ".join(`len(x[0].split())`+' '+x[1]for x in re.findall(r"((\d+)( \2)*)",R()))

gnibbler

Posted 2011-02-18T08:18:14.117

Reputation: 14 170

Why do you add two Python answers, where the other one is better then this one? – FUZxxl – 2011-02-18T19:49:58.917

3@FUZxxl: 2 different methods of doing it. – JPvdMerwe – 2011-02-18T20:49:39.270

2

Golfscript - 40 39

~-1](;(:<1\@{:b={)<}{' '<' '1b:<}if}/;;

Arnaud Le Blanc

Posted 2011-02-18T08:18:14.117

Reputation: 2 286

1you can leave the [ out, the ] will push one for you automatically – gnibbler – 2011-02-19T09:23:27.490

2

CJam, 11 bytes (non-competing)

l;l~]e`e_S*

CJam interpreter online (test case provided by this comment).

Explanation:

l;l~]e`e_S*
l           Get input line
 ;          Remove ToS
  l         Get input line
   ~        Evaluate code
    ]       Wrap the stack in an array (from [-mark)
     e`     Run-length encode
       e_   Flatten
         S  Space (' ')
          * Join

Erik the Outgolfer

Posted 2011-02-18T08:18:14.117

Reputation: 38 134

2

Retina, 23 bytes (non-competing)

Since this challenge pre-dates Retina, this answer is non-competing.

Thanks to @MartinEnder who helped me save 23 (!!) bytes and helped me understand some new features to (hopefully) produce better examples of Retina code in the future!

A1`
(\d+)( \1|)*
$#2 $1

Explanation

The first line is AntiGrep with a limit of 1 to remove the first line. Then we match any integer followed by either a space and the same integer or nothing which handles our count correctly, so we can just replace with the number of matches of group 2 ($#2), and the original integer ($1).

Try it online!

Dom Hastings

Posted 2011-02-18T08:18:14.117

Reputation: 16 415

I think you can remove the third and fourth lines if you change the fifth to (\d+)( \1)*. – ETHproductions – 2016-11-02T16:57:52.007

@ETHproductions and the last two... How did I not see that :| Thanks! – Dom Hastings – 2016-11-02T17:04:21.143

...except now it fails on inputs containing 111, 1111, etc. :| – ETHproductions – 2016-11-02T17:08:28.947

@ETHproductions Hmmm, but only if they\re the last numbers?! Sad. Ok, I'll delete and play. Thanks! – Dom Hastings – 2016-11-02T18:24:33.737

@ETHproductions Can't figure that out right now, so I've rolled back to your revision and I'll probably take a look after I've eaten. That'll probably help! :) – Dom Hastings – 2016-11-02T18:33:24.643

I believe this works: http://retina.tryitonline.net/#code=QTFgCihcZCspKCBcMXwpKgokIzIgJDE&input=MzYKMSAxIDEgMSAxIDEgMyAzIDMgMyAzIDIgMiA3IDcgNyA3IDQgNCA5IDkgOSA5IDkgOSA5IDkgOSA0IDQgOCAzIDMgMyAzIDA While this is a lot shorter it just resulted from a few incremental improvements to your idea: use antigrep to discard the first line, move the space into the second group to avoid append and deleting a space at the end, then originally I incremented group 2 with an additional (?<2>) to avoid the unary, but adding an empty alternative also does the trick.

– Martin Ender – 2016-11-10T14:12:16.420

Amazing... halfed the by count. I knew I didn't like the duplication of the unary. Using the empty group handling that is clever. AntiGrep makes sense, I didn't get it when I first read the docs, but that's probably just concentration lapse. Amazing updates for sure. Thank you for sharing. I'll try and retain this information! – Dom Hastings – 2016-11-10T18:41:43.237

1

Scala - 141 chars

a purely functional scala solution. i am using 2 hacks here (beginning and end markers = "a", have to cut them off at the end :()

"a" is the program parameter

the full working code is

package hstar.randomstuff

import tools.nsc.io.File

/**
 * Developed with pleasure :)<br>
 * User: HoD<br>
 * Date: 28.07.11<br>
 * Time: 19:37<br>
 */

object RLEEncoder {
  def main(a: Array[String]) {
   print(((0,"","")/:(File(a(0)).lines.toSeq(1).split(' '):+"a"))((a,c)=>if(a._2!=c)(0,c,a._3+' '+a._1+' '+a._2)else(a._1+1,c,a._3))._3.drop(4))
  }
}

the actual logic is

print(((0,"","")/:(File(a(0)).lines.toSeq(1).split(' '):+"a"))((a,c)=>if(a._2!=c)(0,c,a._3+' '+a._1+' '+a._2)else(a._1+1,c,a._3))._3.drop(4))

hamsterofdeath

Posted 2011-02-18T08:18:14.117

Reputation: 11

Cool! Don't forget to mention it is Scala. And prepend the title with # to get it big. – PhiLho – 2011-07-28T19:44:46.950

Mmm, I had to change File(a(0)) to tools.nsc.io.File(args(0)), so that's 157 chars. And it counts one less than needed. – PhiLho – 2011-07-28T19:53:26.320

Note: with the changes I suggest, your line works as a Scala script: scala RLE.scala input.txt; the off-by-one error is fixed with (1,c,a._3 instead of (0,c,a._3 – PhiLho – 2011-07-28T20:20:39.743

Add a line of equal signs (=) underneath the title to turn it into a title. – Daniel C. Sobral – 2011-07-29T12:45:35.943

@PhiLho If it's a one-liner script, one can use scala -e '...' to run it! :-) – Daniel C. Sobral – 2011-07-29T12:48:23.663

0

Stacked, noncompeting, 34 bytes

([prompt]2*eval)behead flatrle out

Confirms strictly with the IO format. Try it here!

Or, we can use the builtin for 7 bytes: flatrle (or $flatrle).

Conor O'Brien

Posted 2011-02-18T08:18:14.117

Reputation: 36 228

Is this noncompeting? – Zacharý – 2016-12-23T23:30:31.463

@ZacharyT Ah, yes, of course. – Conor O'Brien – 2016-12-23T23:33:49.013

0

Scala - 247 196

(+ 4 separators)

Being a beginner at Scala, I found the challenge interesting for a functional approach, but I fear I haven't took a concise approach. I guess a seasoned Scala coder will make a one liner... Particularly if I missed a useful collection method!

Anyway, here is the golfed version, even if it is out of competition:

readLine;val n=(readLine+" z").split(' ')
def g(v:String)={var a=1
(n:String)=>{if(n==v){a+=1;Nil}else List(a,v)}}
var p=g(n.head)
print(n.tail.flatMap{n=>val r=p(n);if(r!=Nil)p=g(n);r}.mkString(" "))

I feel the newlines should be counted (as spaces) as here they are mandatory separators. But well, the Python scripts above doesn't count them (while they are mandatory too), so I hadn't either...

The uncompressed version, with comments for my own benefit:

// Read the stdin, skipping the first line, returning the second one
// cat input.txt | scala RLE.scala
readLine // Thanks Daniel!
val line = readLine

// Add an arbitrary symbol that is dropped in the process... (trigger last sequence processing)
val numbers = (line + " z").split(' ')
println(numbers mkString " ")

/** Returns a closure to process the given value in the sequence to come */
def getSeq(value: String) =
{
  var acc = 1
  // Make a closure over value and acc
  (n: String) =>
  {
    if (n == value)
    {
      acc += 1
      Nil
    }
    else // Different value, return the cumulation so far
    {
      List(acc, value)
    }
  }
}

var process = getSeq(numbers.head) // Initial closure
/** Processes the numbers in the sequence. */
def accumulate(n: String) =
{
  val p = process(n)
  if (p != Nil) // We got a result
  {
    process = getSeq(n) // We need a fresh function over the new sequence that starts
  }
  p
}

println(numbers.tail.flatMap(accumulate).mkString(" "))

Feels clumsy with ugly hacks (the additional symbol, using head and tail...) but at least it works...

[EDIT] I used some of the tricks shown by Daniel Sobral to shorten a bit my code. I kept my original design/algorithm to be distinctive... :-)

PhiLho

Posted 2011-02-18T08:18:14.117

Reputation: 653

How about:

Console.in.readLine;Console.in.readLine.split(" ").groupBy(f=>f).mapValues(_.size).map(e=>e._2+" "+e._1).mkString(" ")
 – jrudolph  – 2011-07-28T14:07:25.803

Hmm, no that's of course no RLE what I've posted. – jrudolph – 2011-07-28T14:13:26.550

Yeah, I rejected groupBy as it returns a Map, while we need to keep order and to have identical "keys" within the result. The Console.in.readLine is useful, though. – PhiLho – 2011-07-28T18:59:20.237

0

JavaScript, 126 bytes

(x,j=0)=>(x=x.split` `).reduce((a,v,i)=>(v!=x[i-1]?(a[++j]=[]):a[j]).push(v)&&a,[]).reduce((a,v)=>a+v.length+" "+v[0]+" " ,"")

I'm guessing this can be reduced further, but I'm just happy it works.

First I split based on the space, then I convert the array from [1,1,1,2,2,3,3] to a series of array representing each run [[1,1,1],[2,2],[3,3]], then I reduce that again to produce the required output of "3 1 2 2 2 3".

Grax32

Posted 2011-02-18T08:18:14.117

Reputation: 1 282