Line up the Diagonals of a Grid of Text

15

1

Given a rectangular grid of text, line up the diagonals that go from the upper-left to the bottom-right into columns such that the lowest-rightmost characters of all diagonals are on a level. Use spaces for indentation.

For example, if the input grid of text is

abcd
1234
WXYZ

then you'd line up the diagonals W, 1X, a2Y, b3z, c4, and d in columns giving this output:

  ab
 123c
WXYZ4d

Note that the lowest-rightmost characters of all diagonals, WXYZ4d, are at the same level.

Details

  • The input grid of text will be at minimum 1×1 in size and all lines will be the same length.

  • You may take the input grid as a multiline string or as a list of single line strings.

  • The input grid will only contains printable ASCII characters (includes space).

  • The output may optionally have one trailing newline but there should be no other empty lines.

  • The lines of the output may optionally have trailing spaces but should not have unnecessary leading spaces.

Other Examples

Empty lines separate examples. Each input is directly followed by its output.

123
456
789

  1
 452
78963

123.?!
456??!
789!!!

  123.
 456???
789!!!!!

**@
@  

 **
@  @


/\/\
\/ /
/ /\
\/\/

   /
  \/\
 / / /
\/\/\/\

12
34
56
78
90

 7531
908642

Code

Code

G
O
L
F

FLOG

~

~

Scoring

The shortest code in bytes wins.

Calvin's Hobbies

Posted 2016-12-27T09:01:14.790

Reputation: 84 000

Related. Related tip. – Martin Ender – 2016-12-27T09:50:45.167

Can the input be a 2D char array (a matrix of chars)? – Luis Mendo – 2016-12-27T13:14:45.370

Can the first column of the input contain spaces? – user41805 – 2016-12-27T14:56:58.550

@LuisMendo That sounds ok. – Calvin's Hobbies – 2016-12-27T21:29:39.197

@KritixiLithos Yes, it might. – Calvin's Hobbies – 2016-12-27T21:29:52.420

Output as a single string or is a list/array ok? – edc65 – 2016-12-30T11:17:39.797

Answers

4

J, 12 bytes

|./.&.|:&.|.

Defines an anonymous verb. Try it online!

Explanation

|./.&.|:&.|.
|.            Reversed
  /.          anti-diagonals
    &.        under
      |:      transpose
        &.    under
          |.  reversal

In J, u &. v (read: u under v) means "v, then u, then inverse of v". Reversal and transpose are self-inverses, so the program really means "reverse, transpose, extract reversed anti-diagonals, transpose, reverse".

With example input:

abcd
1234
WXYZ

Reverse:

WXYZ
1234
abcd

Transpose:

W1a
X2b
Y3c
Z4d

Extract reversed anti-diagonals (and pad with spaces):

W  
X1 
Y2a
Z3b
4c 
d  

Transpose:

WXYZ4d
 123c 
  ab  

Reverse:

  ab  
 123c 
WXYZ4d

Zgarb

Posted 2016-12-27T09:01:14.790

Reputation: 39 083

2Excellent demonstration of the power of adverbs – miles – 2016-12-27T10:28:51.193

2I woke up and remembered those were actually conjunctions. – miles – 2016-12-27T21:01:01.833

2

CJam, 29 bytes

qN/{)\z}h]2/{~W%+}%eeSf.*W%N*

Try it online!

Explanation

Instead of extracting the diagonals, we peel off layers from the end, alternating left and right. Consider the following input:

GFDB
EEDB
CCCB
AAAA

If we write out the diagonals as required by the challenge, we get:

   G
  EEF
 CCCDD
AAAABBB

Note that this is simply (from bottom to top), the bottom-most row, concatenated with the right-most column. This definition also works if the input is rectangular.

{      e# Run this loop while there are still lines left in the input.
  )    e#   Pull off the bottom-most row.
  \    e#   Swap with the remaining rows.
  z    e#   Transpose the grid so that the next iteration pulls off the last
       e#   column instead. However, it should be noted that transposing
       e#   effectively reverses the column, so the second half of each output
       e#   line will be the wrong way around. We'll fix that later.
}h     e# When the loop is done, we'll have all the individual layers on the
       e# stack from bottom to top, alternating between horizontal and vertical
       e# layers. There will be an empty string on top.
]      e# Wrap all those strings in a list.
2/     e# Split into pairs. There may or may not be a trailing single-element
       e# list with the empty string.
{      e# Map this block over each pair...
  ~    e#   Dump the pair on the stack.
  W%   e#   Reverse the second element.
  +    e#   Append to first element.
       e#   If there was a trailing single-element list, this will simply
       e#   add the empty string to the previous pair, which just removes
       e#   the empty string.
}%
ee     e# Enumerate the list, which pairs each string (now containing both halves)
       e# of an output line from bottom to top) with its index.
Sf.*   e# Turn those indices X into strings of X spaces, to get the correct
       e# indentation.
W%     e# Reverse the list of strings so the longest line ends up on the bottom.

Martin Ender

Posted 2016-12-27T09:01:14.790

Reputation: 184 808

Careful, that ] will wrap the entire stack! I think functions should work regardless of the stack contents beneath the input, and you seem to agree ^^

– Lynn – 2016-12-30T15:49:17.600

@Lynn whoops, forgot that I was using ] when I changed it to a function. – Martin Ender – 2016-12-30T15:53:01.693

I think you could do [{)\z}h] and keep it a function, for 27 bytes. – Lynn – 2016-12-30T16:37:18.430

2

JavaScript, 116 101 bytes

f=(s,r='$&',l='',z=s.replace(/.$|\n?(?!.*\n)..+/gm,x=>(l=x+l,'')))=>l?f(z,r+' ')+l.replace(/\n?/,r):l


G.onclick=()=>O.textContent=f(I.value);
<textarea id=I style=height:100px>/\/\
\/ /
/ /\
\/\/</textarea><button id=G>Go</button><pre id=O></pre>

I just wanted to use this regex pattern /.$|\n?(?!.*\n)..+/gm idea. (https://regex101.com/r/mjMz9i/2)

JavaScript regex flavour is disappointing, I had to use (?!.*\n) because it doesn't have \Z implemented, and somehow I didn't get to use \0.

  • 15 bytes off thanks @Neil.

Washington Guedes

Posted 2016-12-27T09:01:14.790

Reputation: 549

I just love this approach, but you can use . instead of [^] since you only need to skip non-newline characters to find a newline, which saves 2 bytes. – Neil – 2017-01-01T18:52:53.513

I don't think the ^ is necessary in the final regex, because any \n is already at the beginning of the string anyway, so that saves another byte. – Neil – 2017-01-01T18:54:42.490

I've come up a way to golf the '$&'+' '.repeat(n). Basically that expression is just $& but with a space added each call, which is trivial to implement recursively - replace n=0 with r='$&' and f(z,n+1) with f(z,r+' ') and then r is the desired replacement string. If I've counted correctly that saves 12 bytes. – Neil – 2017-01-01T18:58:31.610

@Neil. That's awesome!!, thank you – Washington Guedes – 2017-01-01T19:24:13.743

2

Jelly, 11 or 10 bytes

ZŒDṙLUz⁶ṚUY

Try it online!

A fairly different algorithm from my other solution; this one uses a builtin to get at the diagonals, rather than doing things manually.

Explanation:

ZŒDṙLUz⁶ṚUY
Z           transpose
 ŒD         diagonals, main diagonal first
    L       number of lines in the original array
   ṙ        rotate list (i.e. placing the short diagonal first)
     U      reverse inside lines
      z⁶    transpose, padding with spaces
        ṚU  rotate matrix 180 degrees
          Y (possibly unnecessary) join on newlines

The diagonals come out in possibly the worst possible orientation (requiring repeated transpositions, reversals, and rotations), and in the wrong order (Jelly outputs the main diagonal first, so we have to move some diagonals from the end to the start to get them in order). However, this still comes out shorter than my other Jelly solution.

user62131

Posted 2016-12-27T09:01:14.790

Reputation:

1

Jelly, 15 or 14 bytes

L’⁶x;\Ṛ;"µZUZṚY

Try it online!

This is an algorithm that doesn't use Jelly's built-in for diagonals. Doing that might make it shorter; I might well try that next.

Here's how the algorithm works. Let's start with this input:

["abc",
 "def",
 "ghi"]

We start off with L’⁶x;\. L’ gives us the length of the input minus 1 (in this case, 2). Then ⁶x gives us a string of spaces of that length (" " in this case); and ;\ gives us the cumulative results when concatenating it (a triangle of spaces). We then reverse the triangle and concatenate it to the left side of the original (;" concatenates corresponding elements of lists, µ forcibly causes a break in the parsing and thus uses the original input as the second list by default), giving us this:

["  abc",
 " def",
 "ghi"]

This is almost the solution we want, but we need to move the elements downwards to be flush with the last string. That's a matter of transposing (Z), reversing inside each line (U), transposing again (Z), and reversing the lines ():

["  abc",
 " def",
 "ghi"]

transpose

["  g",
 " dh",
 "aei",
 "bf",
 "c"]

reverse within rows

["g  ",
 "hd ",
 "iea",
 "fb",
 "c"]

transpose

["ghifc",
 " deb",
 "  a"]

reverse the rows

["  a",
 " deb",
 "ghifc"]

Finally, Y joins on newlines. It's unclear to me whether or not this is required to comply with the specification (which allows input as a list of strings, but doesn't say the same about output), so the exact byte count depends on whether it's included or omitted.

user62131

Posted 2016-12-27T09:01:14.790

Reputation:

1

Pyth, 16 bytes

j_.t_M.Tm+*;l=tQ

Big Pyth:

join-on-newline
reverse transpose-and-fill-with-spaces reverse func-map transpose-justified
map
  plus
    times innermost-var
      length assign tail input
    implicit-innermost-var
  implicit-input

Since people say golfing languages are hard to read, I've designed Big Pyth, which is both easily readable and easily translatable to Pyth. The linked file translates an input stream of Big Pyth to Pyth. Each whitespace-separated Big Pyth token corresponds to a Pyth token, either a character or a . followed by a character. The exceptions are the implicit tokens, which are implicit in the Pyth code.

I want to see how good an explanatory format Big Pyth is, so I'm not going to give any other explanation. Ask me if you want something explained, however.

isaacg

Posted 2016-12-27T09:01:14.790

Reputation: 39 268

0

JavaScript (ES6), 140 bytes

a=>[...Array(m=(h=a.length)<(w=a[0].length)?h:w)].map((_,i)=>[...Array(h+w-1)].map((_,j)=>(a[x=i+h-m-(++j>w&&j-w)]||``)[x+j-h]||` `).join``)

Takes input and output as arrays of strings. Also accepts a two-dimensional character array input, and save 7 bytes if a two-dimensional character array output is acceptable. Explanation: The height of the result m is the minimum of the height h and width w of the original array, while the width is simply one less than the sum of the height and width of the original array. The source row for the characters on the main portion of the result come directly from the appropriate row of the original array, counting up from the bottom, while on the extra portion of the result the source row moves up one row for each additional column. The source column for both halves of the result turns out to be equal to the destination column moved one column left for each source row above the bottom.

Neil

Posted 2016-12-27T09:01:14.790

Reputation: 95 035

0

Octave, 57 bytes

@(A){B=spdiags(A),C=B>0,D='  '(C+1),D(sort(C))=B(C),D}{5}

Rainer P.

Posted 2016-12-27T09:01:14.790

Reputation: 2 457

0

Python 3, 247 bytes

def a(s):
 s=s.split('\n')
 for i,l in enumerate(s):r=len(s)-i-1;s[i]=' '*r+l+' '*(len(s)-r-1)
 print(*[''.join(i) for i in list(zip(*[''.join(a).rstrip([::-1].ljust(min(len(s),len(s[0])))for a in zip(*[list(i)for i in s])]))[::-1]],sep='\n')`

Cormac

Posted 2016-12-27T09:01:14.790

Reputation: 101

Useless whitespace at join(i) for. – Yytsi – 2016-12-30T13:48:48.127

0

Python 2, 150 bytes

def f(l):w=len(l[0]);h=len(l);J=''.join;R=range;print'\n'.join(map(J,zip(*['%%-%ds'%h%J(l[h+~i][j-i]for i in R(h)if-w<i-j<1)for j in R(h-~w)]))[::-1])

Takes input as list of strings.

Arfie

Posted 2016-12-27T09:01:14.790

Reputation: 1 230

0

Clojure, 194 bytes

Implemented the hard way by grouping characters to G and then generating lines.

#(let[n(count %)m(count(% 0))G(group-by first(for[i(range n)j(range m)][(min(- n i)(- m j))((% i)j)]))](apply str(flatten(for[k(reverse(sort(keys G)))][(repeat(dec k)" ")(map last(G k))"\n"]))))

Takes input as a vec of vecs like [[\a \b \c \d] [\1 \2 \3 \4] [\W \X \Y \Z]]. Example:

(def f #( ... ))
(print (str "\n" (f (mapv vec(re-seq #".+" "abcd\n1234\nWXYZ")))))

  ab
 c123
d4WXYZ

NikoNyrh

Posted 2016-12-27T09:01:14.790

Reputation: 2 361