18
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ May the output have trailing whitespace? \$\endgroup\$ – orlp Jul 16 '15 at 14:07
  • \$\begingroup\$ @orlp No it may not, will clarify. \$\endgroup\$ – Sp3000 Jul 16 '15 at 14:10

30 Answers 30

12
\$\begingroup\$

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

\$\endgroup\$
8
\$\begingroup\$

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.

\$\endgroup\$
  • 2
    \$\begingroup\$ Can you please give a small explanation to this? This is very weird to me! \$\endgroup\$ – bobbel Jul 16 '15 at 17:23
  • 2
    \$\begingroup\$ @bobbel Explanation added. \$\endgroup\$ – orlp Jul 16 '15 at 17:43
  • \$\begingroup\$ Really great, thanks! Never heard about it! To try this online I found: pyth.herokuapp.com/… \$\endgroup\$ – bobbel Jul 16 '15 at 17:59
8
\$\begingroup\$

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)

\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – Toby Speight Jul 16 '15 at 20:57
7
\$\begingroup\$

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.

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

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
\$\endgroup\$
  • \$\begingroup\$ 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) \$\endgroup\$ – Glen O Jul 17 '15 at 6:17
  • \$\begingroup\$ 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 \$\endgroup\$ – Glen O Jul 17 '15 at 6:28
  • \$\begingroup\$ 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). \$\endgroup\$ – Glen O Jul 17 '15 at 6:46
  • \$\begingroup\$ @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. \$\endgroup\$ – Alex A. Jul 17 '15 at 14:33
  • \$\begingroup\$ Hey, thanks for mentioning that - I'm not good with regex, but it turned out that \S is useful for my solution, too. \$\endgroup\$ – Glen O Jul 17 '15 at 16:00
4
\$\begingroup\$

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));
}
\$\endgroup\$
4
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ 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). \$\endgroup\$ – ThisSuitIsBlackNot Jul 16 '15 at 20:56
  • \$\begingroup\$ /me heads off to look up -00p; thanks @ThisSuit \$\endgroup\$ – Toby Speight Jul 16 '15 at 21:01
3
\$\begingroup\$

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):]
\$\endgroup\$
  • 1
    \$\begingroup\$ A slightly shorter way to count leading spaces is x.find(x.strip()). \$\endgroup\$ – xnor Jul 16 '15 at 19:49
  • \$\begingroup\$ @xnor good call, thanks! I've been waiting for a 60 byte solution from you all day ;P \$\endgroup\$ – Kade Jul 16 '15 at 19:52
  • \$\begingroup\$ input() in Python 2 would choke on this data. \$\endgroup\$ – Steven Rumbalski Jul 16 '15 at 21:10
  • \$\begingroup\$ @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. \$\endgroup\$ – Kade Jul 16 '15 at 21:11
  • \$\begingroup\$ This program is sad: ): \$\endgroup\$ – HyperNeutrino Apr 8 '17 at 21:07
2
\$\begingroup\$

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).

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

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
\$\endgroup\$
  • 2
    \$\begingroup\$ How about f=->t{t.gsub /^#{t.scan(/^ */).min}/,""}? \$\endgroup\$ – Ventero Jul 16 '15 at 21:00
  • \$\begingroup\$ That's great, @Ventero. Thank you. \$\endgroup\$ – manatwork Jul 17 '15 at 18:37
1
\$\begingroup\$

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)));}
\$\endgroup\$
  • \$\begingroup\$ @ProgramFOX I hadn't seen your solution until after I refreshed the page btw :o) \$\endgroup\$ – Sok Jul 16 '15 at 14:27
1
\$\begingroup\$

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])
\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ It seems like it'd be shorter to write a Regular Expression literal as a string and then eval it \$\endgroup\$ – Downgoat Jul 16 '15 at 16:55
  • \$\begingroup\$ @vihan1086 you might be right. Let me give it a try. \$\endgroup\$ – Optimizer Jul 16 '15 at 16:59
1
\$\begingroup\$

K, 31 bytes

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

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

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

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
\$\endgroup\$
1
\$\begingroup\$

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);
\$\endgroup\$
1
\$\begingroup\$

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
\$\endgroup\$
  • 2
    \$\begingroup\$ A few simple golf changes brings this down to 56 in my count: cut -c$[`grep -o '^ *'<<<"$s"|sort|sed q|wc -c`]-<<<"$s" \$\endgroup\$ – Digital Trauma Jul 16 '15 at 18:15
  • 1
    \$\begingroup\$ @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! \$\endgroup\$ – Thor Jul 17 '15 at 12:25
  • 2
    \$\begingroup\$ Regarding head -n1 vs sed q, there is a line tool in the util-linux package. \$\endgroup\$ – manatwork Jul 17 '15 at 12:43
  • \$\begingroup\$ @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". \$\endgroup\$ – Thor Jul 17 '15 at 12:52
1
\$\begingroup\$

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
> 
\$\endgroup\$
  • \$\begingroup\$ Hey, congrats on 3k rep! \$\endgroup\$ – Alex A. Jul 17 '15 at 16:24
  • \$\begingroup\$ @AlexA. Cheers, didn't think it was important to me ... but :) \$\endgroup\$ – MickyT Jul 17 '15 at 20:58
  • \$\begingroup\$ You mean your life doesn't revolve around fake Internet points? :P \$\endgroup\$ – Alex A. Jul 17 '15 at 20:59
  • \$\begingroup\$ @AlexA. Hopefully not :) congrats on 6k \$\endgroup\$ – MickyT Jul 17 '15 at 21:01
1
\$\begingroup\$

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
\$\endgroup\$
1
\$\begingroup\$

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
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 10 bytes

|©ζ®gð*Ûζ»

Try it online!

\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – Kevin Cruijssen Aug 10 '18 at 15:17
  • \$\begingroup\$ 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. \$\endgroup\$ – Mr. Xcoder Aug 10 '18 at 15:19
0
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ 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]). \$\endgroup\$ – manatwork Jul 17 '15 at 19:53
  • \$\begingroup\$ 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".) \$\endgroup\$ – manatwork Jul 18 '15 at 9:34
0
\$\begingroup\$

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?

\$\endgroup\$
  • \$\begingroup\$ Yes, using the line with the fewest leading spaces is correct. \$\endgroup\$ – Sp3000 Jul 19 '15 at 13:17
0
\$\begingroup\$

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.

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

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
\$\endgroup\$
0
\$\begingroup\$

Stax, 9 bytes

ü═≤+Σî║┘`

Run and debug it

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

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"
\$\endgroup\$
-1
\$\begingroup\$

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>

\$\endgroup\$
  • 9
    \$\begingroup\$ It would be great if the downvoter could explain… \$\endgroup\$ – rink.attendant.6 Jul 16 '15 at 17:01
-1
\$\begingroup\$

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>

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

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>

\$\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.