Remove common leading spaces

19

1

When coding in Python, sometimes you want a multiline string within a function, e.g.

def f():
    s = """\
    Line 1
    Line 2
    Line 3"""

(The backslash is to remove a leading newline)

If you try to actually print out s, however, you'll get

    Line 1
    Line 2
    Line 3

That's not what we want at all! There's too much leading whitespace!

The challenge

Given a multiline string consisting of only alphanumeric characters, spaces and newlines, remove all common spaces from the beginning of each line. Each line is guaranteed to have at least one non-space character, and will have no trailing spaces. The output may not have extraneous whitespace, whether it be before or after the entire output or an individual line (with the exception of a single optional trailing newline).

Input may be via STDIN or function argument, and output may be via STDOUT or function return value. You cannot use any builtins which are designed to dedent multiline strings or perform this exact task, e.g. Python's textwrap.dedent.

This is , so the solution in the fewest bytes wins. Standard loopholes apply.

Test cases

"a"                                  ->   "a"
"   abc"                             ->   "abc"
"   abc\n def\n  ghi"                ->   "  abc\ndef\n ghi"
"    a\n    b\n    c"                ->   "a\nb\nc"
"    a\n    b\n    c\nd"             ->   "    a\n    b\n    c\nd"
"   a   b\n     c     d\n    e f"    ->   "a   b\n  c     d\n e f"

For example, the last test case is

   a   b
     c     d
    e f

and should look like this after stripping leading spaces:

a   b
  c     d
 e f

Sp3000

Posted 2015-07-16T13:46:48.830

Reputation: 58 729

May the output have trailing whitespace? – orlp – 2015-07-16T14:07:51.200

@orlp No it may not, will clarify. – Sp3000 – 2015-07-16T14:10:15.177

Answers

12

CJam, 20 14 bytes

qN/_z{S-}#f>N*

Algorithm:

  • We first split the input on newlines and take a copy (qN/_)
  • Then the smallest column with non space character is calculated by transposing the newline separated array and then simply looking for the index of the first non-all-space row (z{S-}#)
  • Then we simply remove that many characters away from each line (f>)
  • Finally, we join by newline again (N*)

Code Expansion

qN/               e# Read the entire input and split it on newline
   _z             e# Take a copy and transpose rows with columns.
                  e# Now we would have a bunch of all space rows. These rows are the ones
                  e# we want to remove (in form of columns) 
     {  }#        e# Get the index of the first item from the transposed array that returns
                  e# true for this block
      S-          e# From each part, remove spaces. If the part is all-space, it will return
                  e# an empty string, which is false in CJam. We finally will get the index
                  e# of the first non-all-space row (or column)
          f>      e# We take that index and remove that many characters from starting of each
                  e# row of the initial newline separated input
            N*    e# Join the array back using newlines and automatically print the result

Try it online here

Optimizer

Posted 2015-07-16T13:46:48.830

Reputation: 25 836

8

Pyth, 19 18 17 14 bytes

jbu>R!rhCG6G.z

The implementation is pretty cool.

  1. u .z grabs all lines of stdin in an array, puts it in G. Then it evaluates the inner body, puts the result in G, and keeps doing this until it no longer changes (fixed point).

  2. !rhCG6 transposes G, gets the first element of the transposed array (the first column), strips it of any whitespace, and checks if there are any non-whitespace characters left.

  3. The value from 2 is a boolean, which can be seen as an int 0 or 1. >R G grabs this number and slices off that many characters off the left of each line in G. Step 1, 2 and 3 combined basically means that it will keep stripping off columns of whitespace until there is no pure whitespace column left.

  4. jb joins the array of lines by newlines and prints it.

orlp

Posted 2015-07-16T13:46:48.830

Reputation: 37 067

2Can you please give a small explanation to this? This is very weird to me! – bobbel – 2015-07-16T17:23:12.950

2@bobbel Explanation added. – orlp – 2015-07-16T17:43:00.297

Really great, thanks! Never heard about it! To try this online I found: https://pyth.herokuapp.com/?code=jbu%3ER%21rhCG6G.z&input=+++a+++b%0A+++++c+++++d%0A++++e+f&debug=1

– bobbel – 2015-07-16T17:59:48.127

8

sed - 26 bytes

:;/(^|\n)\S/q;s/^ //mg;b

run with -rz

Pretty straightforward:

  /(^|\n)\S/q;           - quit if there is a line that starts with non-space
              s/^ //mg;  - remove exactly one space in each line
:;                     b - repeat

-r option turns on extended regexps, -z reads whole input as a single string (actually uses NUL-byte as line delimiter)

aragaer

Posted 2015-07-16T13:46:48.830

Reputation: 431

Don't you need :;N;$!b or similar to begin with, to gather the input lines into a single pattern space? Edit: no you don't; that's what the -z flag is for. – Toby Speight – 2015-07-16T20:57:15.887

You can golf this to :;/^\S/M!s/^ //mg;t, now not requiring -r – user41805 – 2019-12-18T07:19:59.343

7

SWI-Prolog, 233 223 217 bytes

a(A):-b(A,0,0,0,N),w(A,N,0).
b([A|T],P,K,M,N):-P=1,(A=10,b(T,0,0,M,N);b(T,1,0,M,N));A\=32,(M=0;K<M),b(T,1,0,K,N);I=K+1,b(T,0,I,M,N).
b(_,_,_,N,N).
w([A|T],N,P):-P<N,A=32,Q=P+1,w(T,N,Q);put(A),A=10,w(T,N,0);w(T,N,P);!.

Edit: Completely changed my answer. It now uses character codes instead of strings.

An example of calling this would be a(` a b\n c d\n e f`)., with backquotes. You may need to use double quotes " instead if you have an old SWI-Prolog distrib.

Fatalize

Posted 2015-07-16T13:46:48.830

Reputation: 32 976

5

Julia, 93 92 81 bytes

Saved 10 bytes thanks to Glen O.

s->for i=(p=split(s,"\n")) println(i[min([search(j,r"\S")[1]for j=p]...):end])end

This creates an unnamed function that accepts a string and prints to stdout.

Ungolfed + explanation:

function f(s)
    # Split s into an array on newlines
    p = split(s, "\n")

    # Get the smallest amount of leading space by finding the
    # position of the first non-space character on each line
    # and taking the minimum
    m = min([search(j, r"\S")[1] for j in p]...)

    # Print each line starting after m
    for i in p
        println(i[m:end])
    end
end

Alex A.

Posted 2015-07-16T13:46:48.830

Reputation: 23 761

You can save some space by looking for the first non-space, rather than counting the number of spaces. Rather than minimum([length(search(j, r"^ +")) for j in p])+1, use minimum([search(j,r"[^ ]")[1]for j=p]). Since the challenge states that all lines will have non-space text, it's safe, and saves you 9 bytes (including 3 saved by using = instead of in). Still looking to see if more can be saved. (I wish I could drop the [1], but search produces an enumerator array of type Any, while minimum requires an Int type) – Glen O – 2015-07-17T06:17:40.463

Excuse the mistake above - apparently, I've used up my edits - it's not 9 bytes, but 6, because I failed to note that you'd used = in the golfed form. Anyway, I can save two more characters by defining p in starting the for loop: s->for i=(p=split(s,"\n")) println(i[minimum([search(j,r"[^ ]")[1]for j=p]):end])end – Glen O – 2015-07-17T06:28:54.833

OK, here's another one to shave a bit more - rather than using minimum(x) when x is an array, use min(x...), for one extra byte saved (I'm going to add this one to my list of Julia golfing tips). – Glen O – 2015-07-17T06:46:16.827

@GlenO Nice, thanks for the suggestions. Also, since Julia uses PCRE, non-space characters can be matched with \S rather than [^ ], which saves a byte. – Alex A. – 2015-07-17T14:33:03.043

Hey, thanks for mentioning that - I'm not good with regex, but it turned out that \S is useful for my solution, too. – Glen O – 2015-07-17T16:00:58.250

4

Java, 159

Because there's a conspicuous lack of Java...

void f(String...a){int s=1<<30,b;a=a[0].split("\n");for(String x:a)s=(b=x.length()-x.trim().length())<s?b:s;for(String x:a)System.out.println(x.substring(s));}

It's just loops comparing length to trimmed length, then spitting out substrings. Nothing too fancy. For the scrollbar-impaired:

void f(String...a){
    int s=1<<30,b;
    a=a[0].split("\n");
    for(String x:a)
        s=(b=x.length()-x.trim().length())<s?b:s;       
    for(String x:a)
        System.out.println(x.substring(s));
}

Geobits

Posted 2015-07-16T13:46:48.830

Reputation: 19 061

4

Perl, 47 33

Thanks @ThisSuitIsBlackNot for suggestion to use Perl's implicit loop

#!/usr/bin/perl -00p
/^( +).*(\n\1.*)*$/&&s/^$1//mg

The above is scored as 30 bytes for the line of code + 3 for 00p flags.

Original version, as a function:

sub f{$_=@_[0];/^( +).*(\n\1.*)*$/&&s/^$1//mgr}

This puts the argument into $_, then attempts to greedily match whitespace that's present on all lines with /^( +).*(\n\1.*)*$/ - if successful, $1 now contains the longest common prefix, and we execute the replacement s/^$1//mgr to delete it from the beginning of every line and return the resulting string.

Test

$ cat 53219.data
   a   b
     c     d
    e f
$ ./53219.pl <53219.data 
a   b
  c     d
 e f

Toby Speight

Posted 2015-07-16T13:46:48.830

Reputation: 5 058

Very cool. You can shave off some bytes by running on the command line: perl -00pe '/^( +).*(\n\1.*)*$/&&s/^$1//mg' (30 bytes + 3 for 00p). – ThisSuitIsBlackNot – 2015-07-16T20:56:02.503

/me heads off to look up -00p; thanks @ThisSuit – Toby Speight – 2015-07-16T21:01:09.120

3

Python 2, 86 79 75 Bytes

This can almost definitely be shortened some more, but right now it's not bad.

Thanks to xnor for saving 4 bytes!

s=input().split('\n')
for k in s:print k[min(x.find(x.strip())for x in s):]

Kade

Posted 2015-07-16T13:46:48.830

Reputation: 7 463

1A slightly shorter way to count leading spaces is x.find(x.strip()). – xnor – 2015-07-16T19:49:17.127

@xnor good call, thanks! I've been waiting for a 60 byte solution from you all day ;P – Kade – 2015-07-16T19:52:43.680

input() in Python 2 would choke on this data. – Steven Rumbalski – 2015-07-16T21:10:04.693

@StevenRumbalski, I assume that input is surrounded by quotes. I used to add 2 to the byte count to account for this, but multiple people have said that I don't need to. – Kade – 2015-07-16T21:11:24.167

1This program is sad: ): – HyperNeutrino – 2017-04-08T21:07:49.760

3

Ruby: 77 73 70 66 65 58 57 40 characters

f=->t{t.gsub /^#{t.scan(/^ */).min}/,""}

Sample run:

irb(main):001:0> f=->t{t.gsub /^#{t.scan(/^ */).min}/,""}
=> #<Proc:0x00000001855948@(irb):1 (lambda)>

irb(main):002:0> puts f["   a   b\n     c     d\n    e f"]
a   b
  c     d
 e f
=> nil

irb(main):003:0> f["   a   b\n     c     d\n    e f"] == "a   b\n  c     d\n e f"
=> true

manatwork

Posted 2015-07-16T13:46:48.830

Reputation: 17 865

2How about f=->t{t.gsub /^#{t.scan(/^ */).min}/,""}? – Ventero – 2015-07-16T21:00:30.280

That's great, @Ventero. Thank you. – manatwork – 2015-07-17T18:37:02.043

2

C#, 18 + 145 = 163 bytes

Requires (18 bytes):

using System.Linq;

Method (145 bytes):

string R(string s){var l=s.Split('\n');return string.Join("\n",l.Select(x=>string.Concat(x.Skip(l.Select(z=>z.Length-z.Trim().Length).Min()))));}

The method calculates the lowest amount of leading spaces on the lines and creates a new string built of all lines, with N chars skipped (where N is the previously calculated number).

ProgramFOX

Posted 2015-07-16T13:46:48.830

Reputation: 8 017

1

05AB1E, 10 bytes

|©ζ®gð*Ûζ»

Try it online!

Mr. Xcoder

Posted 2015-07-16T13:46:48.830

Reputation: 39 774

Wait, * repeats the string b a amount of times?.. Didn't knew about that feature of *. I usually do s∍ (swap and lengthen) when I want to repeat a certain character. – Kevin Cruijssen – 2018-08-10T15:17:33.410

Yes, indeed, that works for strings, mainly because vectorization doesn't quite make sense in the case of strings and и yields a list of characters. – Mr. Xcoder – 2018-08-10T15:19:28.290

1

C#, 149 bytes total

Practically the same solution as ProgramFOX's, although the number of characters to trim is calculated manually.

using System.Linq;

And the function itself:

string D(string s){var l=s.Split('\n');int i=0;while(l.All(a=>a[i]==' '))i++;return string.Join("\n",l.Select(b=>b.Substring(i)));}

Sok

Posted 2015-07-16T13:46:48.830

Reputation: 5 592

@ProgramFOX I hadn't seen your solution until after I refreshed the page btw :o) – Sok – 2015-07-16T14:27:27.733

1

bash + sed + coreutils, 74, 56, 55

Test data

s="\
   a   b
     c     d
    e f"

Answer

cut -c$[`grep -o '^ *'<<<"$s"|sort|line|wc -c`]-<<<"$s"

Output

a   b
  c     d
 e f

Thor

Posted 2015-07-16T13:46:48.830

Reputation: 2 526

2A few simple golf changes brings this down to 56 in my count: cut -c$[\grep -o '^ *'<<<"$s"|sort|sed q|wc -c`]-<<<"$s"` – Digital Trauma – 2015-07-16T18:15:50.250

1@DigitalTrauma: Nice, I forgot about $[] arithmetic. Using cut for column selection is much better. I have never seen sed q as an alternative to head -n1, it is a good golfing trick. Thanks! – Thor – 2015-07-17T12:25:31.750

2Regarding head -n1 vs sed q, there is a line tool in the util-linux package. – manatwork – 2015-07-17T12:43:25.107

@manatwork: That saves one character, I will use it. Note that it is deprecated and might disappear in the future, this is from deprecated.txt in util-linux's source tree: "Why: useless, nobody uses this command, head(1) is better". – Thor – 2015-07-17T12:52:26.143

1

Python 3, 100

def f(s):t=s.split("\n");return"\n".join([c[min([len(c)-len(c.lstrip(" "))for c in t]):]for c in t])

monopole

Posted 2015-07-16T13:46:48.830

Reputation: 1 559

1

JavaScript, ES6, 89 86 bytes

This one is totally using just RegEx matching and substitutions.

f=x=>eval(`x.replace(/(^|\\n) {${--`
${x}`.match(/\n */g).sort()[0].length}}/g,"$1")`)

// Snippet related stuff
B.onclick=x=>P.innerHTML=f(T.value)
<textarea id=T></textarea><br>
<button id=B>Trim</button>
<pre id=P></pre>

As always, Firefox only, since ES6. Will add ES5 version later.

Optimizer

Posted 2015-07-16T13:46:48.830

Reputation: 25 836

1It seems like it'd be shorter to write a Regular Expression literal as a string and then eval it – Downgoat – 2015-07-16T16:55:54.250

@vihan1086 you might be right. Let me give it a try. – Optimizer – 2015-07-16T16:59:06.437

1

K, 31 bytes

{`0:(&/{(0;#*=x)@*x}'" "=x)_'x}

Takes input a list of strings and prints the result to stdout.

kirbyfan64sos

Posted 2015-07-16T13:46:48.830

Reputation: 8 730

1

Haskell, 52 bytes

unlines.until(any(/=' ').map head)(map tail).lines

Usage example: unlines.until(any(/=' ').map head)(map tail).lines $ " abc\n def\n ghi" -> " abc\ndef\n ghi\n"

How it works:

                                           lines    -- split the input at newlines into a list of lines
        until                                       -- repeat the 2nd argument, i.e.
                                 map tails          -- cut off the heads of all lines
                                                    -- until the the first argument returns "True", i.e.
             any(/=' ').map head                    -- the list of heads contains at least one non-space
unlines                                             -- transform back to a single string with newlines in-between

nimi

Posted 2015-07-16T13:46:48.830

Reputation: 34 639

1

Python, 94/95

lambda (94 bytes):

f=lambda s:'\n'.join(l[min(l.find(l.strip()) for l in s.split('\n')):] for l in s.split('\n'))

def (95 bytes)

def f(s):l=s.split('\n');m=min(i.find(i.strip())for i in l);return '\n'.join(i[m:] for i in l);

TheCrypt

Posted 2015-07-16T13:46:48.830

Reputation: 141

1

R, 118 111 bytes

Using the wonderful string functions of R :) This is similar/same to other solutions already posted. Input is through STDIN and cats to STDOUT.

cat(substring(a<-scan(,'',sep='|'),Reduce(min,lapply(strsplit(a,' '),function(x)min(which(x>''))-1))),sep='\n')

Test and explanation

> cat(substring(a<-scan(,'',sep='|'),Reduce(min,lapply(strsplit(a,' '),function(x)min(which(x>''))-1))),sep='\n')
1:                  a<-scan(,'',sep='|') # get the input lines
2:                                                         strsplit(a,' ') # split lines on spaces
3:                                                  lapply(                ,function(x)min(which(x>''))-1) # get min index - 1 for non space of each line
4:                                      ,Reduce(min,                                                      ) # get the min of those
5:        substring(                                                                                       ) # trim it off
6:    cat(                                                                                                  ,sep='\n') # output each line
7:
Read 6 items
              a<-scan(,'',sep='|') # get the input lines
                                                     strsplit(a,' ') # split lines on spaces
                                              lapply(                ,function(x)min(which(x>''))-1) # get min index - 1 for non space of each line
                                  ,Reduce(min,                                                      ) # get the min of those
    substring(                                                                                       ) # trim it off
cat(                                                                                                  ,sep='\n') # output each line
> 

MickyT

Posted 2015-07-16T13:46:48.830

Reputation: 11 735

Hey, congrats on 3k rep! – Alex A. – 2015-07-17T16:24:17.397

@AlexA. Cheers, didn't think it was important to me ... but :) – MickyT – 2015-07-17T20:58:39.630

You mean your life doesn't revolve around fake Internet points? :P – Alex A. – 2015-07-17T20:59:31.820

@AlexA. Hopefully not :) congrats on 6k – MickyT – 2015-07-17T21:01:08.797

1

Julia, 72 62 61 57 54 49 bytes

g=s->ismatch(r"^\S"m,s)?s:g(replace(s,r"^ "m,""))

Ungolfed:

g(s)=
if ismatch(r"^\S"m,s)       # Determines if there's a newline followed by something other than a space
                            # Note: the m in r"^ "m says to work in multiline mode.
    s                       # If there is, return the string as the final result.
else                        # otherwise...
    m=replace(s,r"^ "m,"")  # Remove first space after each newline, and space at start of string.
    g(m)                    # Feed back into the function for recursion
end

Older solution (57 bytes):

g(s)=ismatch(r"
\S","
"s)?s:g(replace(s,"
 ","
")[2:end])

Original solution (72 bytes):

g(s)=all([i[1]<33for i=split(s,"\n")])?g(replace(s,"\n ","\n")[2:end]):s

Glen O

Posted 2015-07-16T13:46:48.830

Reputation: 2 548

1

k (24 bytes)

Takes a string as an argument and returns a string (with trailing new-line).

{`/:(&//&:'~^s)_'s:`\:x}

Example:

k) f:{`/:(&//&:'~^s)_'s:`\:x};
k) f"   a   b\n     c     d\n    e f"
"a   b\n  c     d\n e f\n

skeevey

Posted 2015-07-16T13:46:48.830

Reputation: 4 139

0

Stacked, noncompeting, 43 bytes

:lines'^ +'match$#'"!MIN' '*0# '^'\+''mrepl

Try it online!

This works by finding the amount of spaces at the beginning of each line ('^ +'match$#'"!), getting the minimum, repeat a space that many times, and replacing that with nothing on each line.

Conor O'Brien

Posted 2015-07-16T13:46:48.830

Reputation: 36 228

0

Vim, 33, 31 bytes

qq/\v%^(\s.*\n?)*%$
:%s/.
@qq@q## Heading ##

Try it online!

Old version:

qq:g/\v%^(\s.*\n?)*%$/%s/.
n@qq@q

James

Posted 2015-07-16T13:46:48.830

Reputation: 54 537

0

Stax, 9 bytes

ü═≤+Σî║┘`

Run and debug it

recursive

Posted 2015-07-16T13:46:48.830

Reputation: 8 616

0

Gawk, 101 100

{match($0,/^( +)/,t);if(t[1]<s||s==""){s=t[1]};z[NR]=$0;}END{for(r in z){sub(s,"",z[r]);print z[r]}}

For example...

cat input.txt | gawk '{match($0,/^( +)/,t);if(t[1]<s||s==""){s=t[1]};z[NR]=$0;}END{for(r in z){sub(s,"",z[r]);print z[r]}}'

Output...

a   b
  c     d
 e f

Rip Leeb

Posted 2015-07-16T13:46:48.830

Reputation: 1 250

Just barely tested hints: don't capture /^( +)//^ +/ (then you will have the needed value in t[0] instead of t[1]); change s==""!s; remove the { and } around the code after if; remove the ; before }; using Gawk-specific function to be able to remove the { and } around the code after for: {sub(s,"",z[r]);print z[r]}print gensub(s,"",1,z[r]). – manatwork – 2015-07-17T19:53:19.973

Sorry to say, but both your original code and the one with my size optimization are failing on input with an unindented line, other than the last one. (For example "␠one\nzero\n␠one\n␠␠two".) – manatwork – 2015-07-18T09:34:34.827

0

C GCC, 74 Bytes

main(_,z){z=1;while(-~(_=getchar()))putchar(_==32&&z?0:(z=_==10?1:0,_));}

Only removes all whitespace, not relating to previous lines, requesting help to finish. ALSO, in terms of common whitespaces, does the OP mean that which line has the fewest leading spaces, that is the number of spaces that is to be removed from each line?

Jake

Posted 2015-07-16T13:46:48.830

Reputation: 1

Yes, using the line with the fewest leading spaces is correct. – Sp3000 – 2015-07-19T13:17:40.957

-1

CoffeeScript, 112 bytes

f=(x)->(a=x.split "\n").map((v)->v[Math.min.apply(null,a.map((v)->(r=/^ +/.exec v)&&r[0].length))...]).join "\n"

rink.attendant.6

Posted 2015-07-16T13:46:48.830

Reputation: 2 776

-1

JavaScript (ES6), 106 98 bytes

The newlines are necessary and are counted as 1 byte each:

f=x=>(a=x.split`
`).map(v=>v.slice(Math.min(...a.map(v=>(r=/^ +/.exec(v))&&r[0].length)))).join`
`

Demo

As with other ES6 answers, they only work in Firefox at the moment.

f=x=>(a=x.split`
`).map(v=>v.slice(Math.min(...a.map(v=>(r=/^ +/.exec(v))&&r[0].length)))).join`
`

// For demonstration purposes
console.log = x => X.innerHTML += x + `\n<hr>`;

console.log(f("a"));
console.log(f("   abc"));
console.log(f("   abc\n def\n  ghi"));
console.log(f("    a\n    b\n    c"));
console.log(f("    a\n    b\n    c\nd"));
console.log(f("   a   b\n     c     d\n    e f"));
<pre id=X></pre>

rink.attendant.6

Posted 2015-07-16T13:46:48.830

Reputation: 2 776

11It would be great if the downvoter could explain… – rink.attendant.6 – 2015-07-16T17:01:28.113

-1

JavaScript ES6, 85 bytes

s=>s.split`
`.map(z=>z.slice(Math.min(...s.match(/^ */gm).map(l=>l.length)))).join`
`

The new lines are significant

ES5 Demo:

function t(s) {
  return s.split("\n").map(function(z) {
    return z.slice(Math.min.apply(0, s.match(/^ */gm).map(function(l) {
      return l.length;
    })));
  }).join('');
}

// Demo
document.getElementById('go').onclick = function() {
  document.getElementById('r').innerHTML = t(document.getElementById('t').value)
};
Input:
<br>
<textarea id="t"></textarea>
<br>
<button id="go">Run</button>
<br>Output:
<br>
<pre style="background-color:#DDD;" id="r"></pre>

Downgoat

Posted 2015-07-16T13:46:48.830

Reputation: 27 116

-1

JavaScript (ES6) 56

Recursive, trying to remove one space at a time from each row until a non-space is found.

Test running the snippet below - being ES6, Firefox only

f=s=>(r=s.replace(/^./gm,x=>(k|=x>' ',''),k=0),k?s:f(r))

// Test
test=
[[ "a", "a" ]
,["   abc", "abc" ]
,["   abc\n def\n  ghi", "  abc\ndef\n ghi" ]
,["    a\n    b\n    c", "a\nb\nc" ]
,["    a\n    b\n    c\nd", "    a\n    b\n    c\nd" ]
,["   a   b\n     c     d\n    e f","a   b\n  c     d\n e f" ]]

var tb=''
test.forEach(t=>{
  t[2]=f(t[0])
  t[3]=t[2]==t[1]?'OK':'FAIL'
  tb+='<tr><td>'+t.join('</td><td>')+'</td></tr>'
})
B.innerHTML=tb
td { white-space: pre; font-family: monospace; border: 1px solid#444; vertical-align:top}
#I,#O { height:100px; width: 200px }
<b>Your test:</b>
<table><tr><td><textarea id=I></textarea></td>
<th><button onclick='O.innerHTML=f(I.value)'>-></button></th>
<td id=O></td></tr></table>
<b>Test cases:</b><br>
<table ><tr><th>Input</th><th>Expected</th><th>Output</th><th>Result</th></tr>
<tbody id=B></tbody></table>

edc65

Posted 2015-07-16T13:46:48.830

Reputation: 31 086