31
\$\begingroup\$

Aligning Lines!

Given a character and a multiline string, your job is to pad each line of the string so they line up among the given delimiter.

Examples

Input:

,
Programming, Puzzles
And, Code golf

Output:

Programming, Puzzles
        And, Code golf

Input

The input will be a multiline string and a character (which you will align among), you may take these in any order / format you wish. The character will appear exactly once per line. Each line of the input may be different in length.

Input can be through function arguments or STDIN.

Output

The output should be the same string centered. You are allowed one trailing newline and no trailing whitespace.

The output should be padded with the minimum amount of spaces. You may not remove any leading whitespace in the input (if it exists).

Output can be from function return or STDOUT.

\$\endgroup\$
  • \$\begingroup\$ Can input to a full program come from command-line arguments, or is that forbidden? \$\endgroup\$ – DLosc Sep 12 '15 at 17:22
  • \$\begingroup\$ @DLosc Yes, of course \$\endgroup\$ – Downgoat Sep 12 '15 at 17:23
  • \$\begingroup\$ 1. For function/command-line arguments, should we read a single string or would one line per argument be admissible? 2. Do we have to pad the lines with the minimum amount of spaces? \$\endgroup\$ – Dennis Sep 12 '15 at 17:30
  • \$\begingroup\$ @Dennis You may take it in a single string. Or one-line per argument. "you may take these in any order you wish". Yes, you do need to pad the lines with the minimum amount of spaces. I'll edit the spec \$\endgroup\$ – Downgoat Sep 12 '15 at 17:31
  • \$\begingroup\$ @vihan Can functions take in one line per argument? \$\endgroup\$ – xnor Sep 12 '15 at 18:12

23 Answers 23

5
\$\begingroup\$

Pyth, 18 bytes

V.z+*d-eSxRz.zxNzN
\$\endgroup\$
13
\$\begingroup\$

APL (37)

APL just isn't very good at string processing (or I'm not good at golfing, of course).

{⌽∊R,¨' '/⍨¨(⌈/-+)⍺⍳⍨¨⌽¨R←S⊂⍨S=⊃S←⌽⍵}

This takes the character as its left argument, and the multiline string as its right argument. It is assumed that the multiline string ends in a linefeed (e.g. A\nB\nC\n rather than A\nB\nC.) Since I can use "any format [I] wish", and this is also the conventional format for text files, I think this is reasonable.

Explanation:

  • S←⌽⍵: reverse the string, and store it in S.
  • R←S⊂⍨S=⊃S: split S on its first character, and store the array of strings in R.
  • ⍺⍳¨⌽¨R: reverse each string in R, and then find the index of ⍺ (the character) in each string.
  • (⌈/-+): subtract each of the indices from the largest index, giving the amount of spaces needed
  • ' '/⍨¨: for each of those values, generate that many spaces
  • R,¨: add the spaces to each string in R.
  • : join all the strings together
  • : reverse it (to get the original order back)

Example:

      NL←⎕UCS 10 ⍝ newline
      test←'Programming, Puzzles',NL,'And, Code golf',NL
      test ⍝ test string
Programming, Puzzles                
And, Code golf                      

      ⍝ run the function
      +X←','{⌽∊R,¨' '/⍨¨(⌈/-+)⍺⍳⍨¨⌽¨R←S⊂⍨S=⊃S←⌽⍵}test
Programming, Puzzles                        
        And, Code golf                      

      ⍴X ⍝ result is really a string with newlines, not a matrix
44
\$\endgroup\$
9
\$\begingroup\$

CJam, 23 22 20 bytes

Thanks to Dennis for saving 2 bytes.

ea_rf#_:e>\fm.{S*\N}

This reads the lines from command-line arguments and the character from STDIN.

The online interpreter doesn't support command-line arguments, but you can test an equivalent version here.

Explanation

ea    e# Get the lines from ARGV.
_rf#  e# Duplicate input, read the character and find index of character in each line.
_:e>  e# Duplicate indices and find maximum.
\fm   e# Subtract each index from the maximum index.
.{    e# Apply this block to each pair of line and (max_index - index).
  S*  e#   Get a string with the right amount of spaces.
  \N  e#   Swap spaces with line and push a line feed.
}
\$\endgroup\$
9
\$\begingroup\$

Pip, 22 20 18 + 1 = 19 bytes

Y_@?qMgsX(MXy)-y.g

Takes strings as command-line arguments and delimiter from STDIN (idea borrowed from Martin's CJam answer). Uses -n flag to print output values on separate lines.

                    g is list of cmdline args; s is space (implicit)
    q               Read the delimiter from stdin
 _@?                Construct a lambda function that takes a string and returns
                       the index of the delimiter in it
     Mg             Map that function to each remaining item in g
Y                   Yank the resulting list of indices into the variable y

         (MXy)-y    Take the max of y minus each element in y
       sX           Space, repeated that many times...
                .g  ... concatenated to each item in g
                    Print, newline-separated (implicit, -n flag)

And an example run:

C:\Users\dlosc> pip.py -ne Y_@?qMgsX(MXy)-y.g "Programming, Puzzles" "And, Code golf"
,
Programming, Puzzles
        And, Code golf
\$\endgroup\$
7
\$\begingroup\$

JavaScript ES 2015, 113 bytes

f=(c,s)=>s.split`
`.map((e,_,a)=>' '.repeat(a.map(j=>j.indexOf(c)).reduce((g,h)=>g>h?g:h)-e.indexOf(c))+e).join`
`

Not quite as short as the golfing languages posted so far. Takes input as two function arguments, e.g. f(',','Programming, Puzzles\nAnd, Code golf'). The snippet below is ungolfed and includes an easy method to test.

f=function(c,s){
  return s
    .split('\n')
    .map(function(e,_,a){
      return ' '.repeat(
        a.map(function(f){
          return f.indexOf(c)
        }).reduce(function(g,h){
          return g>h?g:h
        })-e.indexOf(c)
      )+e
    })
    .join('\n')
}

run=function(){document.getElementById('output').innerHTML=f(document.getElementById('char').value,document.getElementById('string').value)};document.getElementById('run').onclick=run;run()
<label>Character: <input type="text" id="char" value="," maxlength="1" /></label>
<textarea id="string" rows="4" cols="30" style="display:block">
Programming, Puzzles
And, Code Golf</textarea><button id="run">Run</button><br />
<pre id="output"></pre>

\$\endgroup\$
6
\$\begingroup\$

Pyth, 27 24 bytes

V.z+.[deSmxdz.z<NJxNz>NJ

Updated for the latest Pyth.

Live demo.

27-byte version

jbm+.[eSmxkz.z<dJxdz\ >dJ.z

Live demo.

\$\endgroup\$
  • \$\begingroup\$ Whenever you see jbm at the start of your answer, you should immediately start thinking about V. \$\endgroup\$ – orlp Sep 13 '15 at 7:20
  • \$\begingroup\$ Or simply remove the b \$\endgroup\$ – Jakube Sep 13 '15 at 21:26
5
\$\begingroup\$

Julia, 117 bytes

f(c,t)=(s=[split(l,c)for l=split(t,"\n")];join(map(i->lpad(i[1],maximum(map(i->length(i[1]),s))," ")*c*i[2],s),"\n"))

Ungolfed:

function f(c::String, t::String)
    # Create an array of arrays by splitting on newlines and
    # then on the given delimiter
    s = [split(l, c) for l in split(t, "\n")]

    # Find the maximum length on the left side of the delimiter
    m = maximum(map(i -> length(i[1]), s))

    # Rejoin on the delimiter and pad each line with spaces,
    # and rejoin this with newlines
    join(map(i -> lpad(i[1], m, " ") * d * i[2], s), "\n")
end
\$\endgroup\$
5
\$\begingroup\$

Python 3, 85 (IDLE 3.2.2, Windows)

c,*s=input().split('\n')
for x in s:print(' '*(max(z.find(c)for z in s)-x.find(c))+x)

Pretty straightforward. This finds the position of the character in the string twice: once to find the max (well, once per line), and once to find the offset. I tried combining these but it was longer.

Python 3 is used for the input unpacking. MY IDLE seems to take multiline strings as input.

\$\endgroup\$
  • \$\begingroup\$ @DLosc Works for me in IDLE pasting in a multiline string. \$\endgroup\$ – xnor Sep 12 '15 at 18:10
  • \$\begingroup\$ Hmm. When I do that (IDLE 3.3.4, Windows 7), c gets the delimiter and s gets an empty list. Subsequent calls to input() return the remaining lines one by one. \$\endgroup\$ – DLosc Sep 12 '15 at 19:00
  • \$\begingroup\$ @DLosc Strange. I'm copy-pasting the string directly from my browser into the Idle prompt. Are you doing the same? IDLE 3.2.2, Windows 7 in case it matters. \$\endgroup\$ – xnor Sep 12 '15 at 19:03
  • \$\begingroup\$ Same. Here's a screenshot... \$\endgroup\$ – DLosc Sep 12 '15 at 19:12
  • \$\begingroup\$ @DLosc Still works for me (screenshot). Though I don't understand what's going on, I'm going to say this is compiler or environment specific behavior, and I edited to try to specify the relevant info. The function version is 3 chars longer in Python 2. \$\endgroup\$ – xnor Sep 13 '15 at 9:38
2
\$\begingroup\$

Matlab / Octave, 106 bytes

Function that uses three separate arguments for character, string, string; and gives result in stdout:

function f(c,s,t)
p=find(s==c)-find(t==c);disp([repmat(32,1,max(-p,0)) s]),disp([repmat(32,1,max(p,0)) t])

Example in Matlab:

>> f(',', 'Programming, Puzzles', 'And, Code golf')
Programming, Puzzles
        And, Code golf

Or try it online with Octave interpreter.

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

Julia, 80 bytes

f(c,s)=(t=split(s,'
');u=[search(i,c)for i=t];join([" "].^(maxabs(u)-u).*t,'
'))

Ungolfed:

function f(c,s)
  # converts multiline string to array of single-line strings
  t=split(s,'\n')

  # creates array of positions of delimiter
  u=[search(i,c)for i=t]

  # Appends appropriate number of spaces to each line
  # (uses elementwise operations to achieve this result)
  v=[" "].^(maxabs(u)-u).*t

  # Recombines array of strings to multiline string and returns
  return join(v,'\n')
end
\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6), 105

Using template strings, the 2 newlines are significant and counted.

Test running the snippet in any EcmaScript 6 compatible browser (that is FireFox. Chrome does not support default parameters)

f=(s,c,p=(s=s.split`
`).map(r=>m<(v=r.indexOf(c))?m=v:v,m=0))=>s.map((r,i)=>' '.repeat(m-p[i])+r).join`
`

// Ungolfed
f=(s,c)=>{
  s=s.split('\n')
  p=s.map(r=>r.indexOf(c))
  m=Math.max(...p)
  s=s.map((r,i)=>' '.repeat(m-p[i])+r)
  return s.join('\n')
}  

// TEST
out=x=>O.innerHTML+=x+'\n'

out(f(`Programming, Puzzles
And, Code golf`,','))
<pre id=O></pre>

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

Python 2, 93 bytes

def f(x,y,z):
 p=y.index(x)-z.index(x)
 if p<0:y=" "*abs(p)+y
 else:z=" "*p+z
 print y+'\n'+z

Called like so:

f(',','Programming, Puzzles','And, Code Golf')
\$\endgroup\$
2
\$\begingroup\$

C# 4.0, 329 320 307 bytes

using System;class P{static void Main(){Func<char,dynamic>f=(d)=>Console.ReadLine().Split(d);var c=f(' ')[0][0];var m=0;var l=new string[9999][];var z=0;for (l[z]=f(c);l[z].Length==2;l[z]=f(c)){m=Math.Max(l[z][0].Length,m);z++;}for(var i=0;i<z;i++){Console.WriteLine("{0,"+m+"}"+c+"{1}",l[i][0],l[i][1]);}}}

Ungolfed version :

using System;
class P
{
    static void Main()
    {
        // lamba to to read a line and split on a char, returns an array of 
        Func<char,dynamic>f=(d)=>Console.ReadLine().Split(d); 
        // read the separator char by taking the first char of the first string 
        // in the array
        // use our lambda
        var c=f(' ')[0][0];
        var m=0; // max position where char is found
        var l=new string[9999][]; // hold all input
        var z=0; // count valid entries in l
        // loop until the input doesn't contain an
        // array with 2 elements
        // here we use our lambda agian, twice
        for (l[z]= f(c);l[z].Length==2;l[z] = f(c))
        {
            // calculate max, based on length 
            // of first element from the string array
            m=Math.Max(l[z][0].Length,m);
            z++; // increase valid items
        }
        // loop over all valid items
        for(var i=0;i<z;i++)
        {
        // use composite formatting with the padding option
        // use the max to create a format string, when max =4 
        // and seperator char is , this will give
        // "{0,4},{1}"
            Console.WriteLine("{0,"+ m +"}"+c+"{1}",l[i][0],l[i][1]);
        }
    }
}

It does accept a maximum of 9999 lines ...

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

Jelly, 12 bytes

Ỵ©w€µạṀ⁶ẋż®Y

Try it online!

Done and golfed with caird coinheringaahing in Jelly Hyper Training (JHT), our Jelly practice chat room.

How it works

The third command line argument (first input) should be the multi-line string, and the character should be the fourth command line argument (second input).

Ỵ©w€µạṀ⁶ẋż®Y  ~ Full program.

Ỵ             ~ Split the string by newlines.
 ©            ~ Copy the result to the register.
  w€          ~ Get the index of the first occurrence of the character on each line.
      Ṁ       ~ Take the maximum.
    µạ        ~ And subtract it from each index, taking the absolute value.
       ⁶ẋ     ~ Repeat a space that many times (vectorizes).
         ż®   ~ Interleave with what was stored in the register.
           Y  ~ Join by newlines and print implicitly.

I am not sure whether taking input as a list of lines is allowed, so this takes a multiline string as input. If it were allowed:

10 bytes

w€µạṀ⁶ẋż³Y

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ that's when you know you have created a successful room \$\endgroup\$ – Erik the Outgolfer Dec 10 '17 at 19:52
2
\$\begingroup\$

Dyalog APL, 22 20 16 bytes

-4 thanks to ngn.

APL is actually not so bad at string processing, if allowed to work with arrays. In this challenge, we may chose the most appropriate format, which for APL means a vector of text vectors as left argument, and the delimiter as a scalar right argument. This even handles multiple delimiters per line, and aligns the first one of each line.

⊣,¨⍨' '⍴¨⍨⌈.⍳-⍳¨

⊣,¨⍨ prepend each line with

' '⍴¨⍨ as many spaces as

⌈.⍳ the rightmost index of the character among the lines

- minus

⍳¨ the index of the character in each line

Try APL online! ( added to print output vertically)

Bonus? Works for any number of strings, and delimiters (aligns by left-most).

\$\endgroup\$
  • \$\begingroup\$ ⊣,¨⍨' '⍴¨⍨⌈.⍳-⍳¨ \$\endgroup\$ – ngn Dec 10 '17 at 21:34
  • \$\begingroup\$ Yes, of course. \$\endgroup\$ – Adám Dec 10 '17 at 21:48
1
\$\begingroup\$

C#, 191

As a function. Roughly a porting of my JS answer.

using System.Linq;string f(string s,char c){var q=s.Split('\n');int m=0,v;Array.ForEach(q,x=>m=m<(v=x.IndexOf(c))?v:m);return String.Join("\n",q.Select(x=>new String(' ',m-x.IndexOf(c))+x));}
\$\endgroup\$
1
\$\begingroup\$

Ruby, 74 bytes

l=lambda{|d,s|s.each{|e|puts ' '*(s.map{|f|f.index(d)}.max-e.index(d))+e}}

and call it like

l.call ',',['Programming, Puzzles','And, Code golf']
\$\endgroup\$
1
\$\begingroup\$

R, 68 bytes

function(c,x,y,r=regexpr)cat(x,"\n",rep(" ",r(c,x)-r(c,y)),y,sep="")

Unnamed function that takes 3 inputs; c which is the character to align, x is the first string and y the second string.

In R, the function regexpr returns the position of a given pattern in a string. The solution works by applying regexpr on both strings and repeating white spaces amounting to the difference and subsequently just print both inputs separated by a newline.

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

Python 2, 67 66 bytes

def a(d,l):
 i=l[0].index(d)
 for e in l:print' '*(i-e.index(d))+e

Called with:

a(',', ['Programming, Puzzles', 'And, Code golf'])
\$\endgroup\$
0
\$\begingroup\$

Moonscript, 138 bytes

(n)=>
 i=0
 @='
'..@
 l=[b-a for a,b in @gmatch "
().-()"..n]
 m=math.max unpack l
 (@gsub '
',(a)->
  i=i+1
  a..(' ')\rep m-l[i])\sub(2)

This returns a function which takes 2 arguments. The first is the string, the second is the character to align on. These arguments are the implicit argument @, and n.

First, I append a new line to the string, to make processing easier.

@='
'..@

Now, I generate a list of the positions of every alignment character, using gmatch. Next, I replace the newline before every line with the correct number of spaces, then trim the newline I added at the beginning.

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

Lua, 169 bytes

function a(d,t)m={}for k,v in pairs(t)do m[#m+1]=string.find(v,d)end o=math.max(unpack(m))for k,v in pairs(t)do print(string.rep(" ",o-(string.find(v,d)or 0))..v)end end

Not as short as other answers but this is my first one :D

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

Retina, 71 bytes

+`^((.)(.*¶)*)((.)*\2.*¶)((?<-5>.)*(?(5)\2|(.)\2).*)
$1$#7$* $4$#5$* $6

Try it online! Note: This leaves the alignment character in the output; it can be deleted at a cost of 4 bytes. If only two strings need to be aligned, then for 52 bytes:

^(.)¶((.)*\1.*¶)((?<-3>.)*(.)*\1.*)
$#5$* $2$#3$* $4

Explanation:

^(.)¶

This matches the alignment character.

((.)*\1.*¶)

This matches the first line and also keeps track of how many characters there were before the alignment character. (.NET keeps a match stack for each variable, in this case, $3.)

((?<-3>.)*(.)*\1.*)

This matches the second line, trying to account for as many characters as we found on the first line. ?<-3> causes the match to pop the stack for each character, until it is empty, at which point the match fails, and the (.)* then matches the remaining characters before the alignment character. At this point we have the following variables:

  • $1 contains the alignment character
  • $2 contains the first line
  • $3 contains a stack whose length is the first line prefix minus the second line prefix
  • $4 contains the second line
  • $5 contains a stack whose length is the second line prefix minus the first line prefix

$#5$* then prefixes the necessary number of spaces to make the first line align with the second, and vice versa for $#3$*.

Similar logic applies to the main answer, except here we have to find two lines which don't align so that we can align them (this is where the ?(5) comes in) and then repeat the alignment over all the lines until they are all equally aligned.

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

Common Lisp, 101 bytes

(lambda(c l)(dolist(x l)(format t"~,,v@a~%"(-(apply'max(mapcar(lambda(x)#1=(position c x))l))#1#)x)))

The first parameter is the character, the second is a list of string to be aligned.

Try it online!

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