# “Stair-ify” a string

You must write a program or function that creates a "stair-ified" string. Here is how you "stair-ify" a string:

For each character in the string:

• If the character is an upper or lowercase vowel, not including 'y', output it then move the rest of the string up a column.

• If the character is a space or a tab, output it then move the rest of the string down a colum.

• If the character is neither, output it normally.

IO can be in any reasonable format. The input will not contain any newlines. If you want, you can remove any trailing whitespace.

If you choose to return the string, rather than printing it, please also include a short program that will print your string so that it can be visualized. This is not mandatory, nor will it go towards your byte-count. This is just a convenience for users who don't understand golf or esolangs (like me) to be able to verify output or tinker with the code.

# Sample IO:

Output for "bcdef ghijkl":

    f    jkl
bcde  ghi


Output for "Programming Puzzles And Code-Golf":

                               lf
-Go
s  nd   de
ng   zzle  A   Co
mmi   Pu
gra
Pro


Output for "Abcdefghijklmnopqrstuvwxyz":

                     vwxyz
pqrstu
jklmno
fghi
bcde
A


As usual, this is code-golf, so shortest answer in bytes wins.

• – DJMcMayhem Apr 8 '16 at 22:58
• May we strip any leading/trailing whitespace? – orlp Apr 8 '16 at 23:24
• @orlp Since it won't change the visual representation at all, I don't see why not. – DJMcMayhem Apr 8 '16 at 23:26
• If we choose to return the string, is the program for printing it included in the byte count? – user31556 Apr 8 '16 at 23:59
• @PeterPeter See my last edit. – DJMcMayhem Apr 9 '16 at 0:04

# MATL, 38 37 bytes

Oj33<G13Y2m-IL)hYstX<-"@Z"GX@)h]Xh!c!


Try it online!

### Explanation

For each char, the code computes its vertical position, measured from above (0 is highest). It then builds the output string transposed: each char is on a line with as many leading spaces as its vertical position indicates. Then all lines are contatenated into a 2D char array, which is finally transposed and displayed.

O       % push a 0
j       % input a string
33<     % array of the same length as the input that contains true for spaces or tabs
G       % push input again
11Y2    % string 'aeiouAEIOU'
m       % array of the same length as the input that contains true for vowels
-       % subtract
IL)     % remove last element
h       % prepend the 0 that is at the bottom of the stack
Ys      % cumulative sum. This gives the vertical position of each char
tX<     % duplicate. Compute minimum
-       % subtract. This sets minimum vertical position to 0
"       % for each vertical position
@     %   push vertical position of current character
Z"    %   string with that many spaces
G     %   push input again
X@)   %   get the character corresponding to the current iteration index
h     %   concatenate horizontally
]       % end for each
Xh      % concatenate all lines into a row cell array
!       % transpose into a column cell array
c       % convert into 2D array, padding with spaces if needed
!       % transpose. Implicitly display


# Pyth, 63 bytes

V_Q aY?}rN0"aeiou"=hZ?}N"     "=tZZ;Jh.mbYKh.MZYjC.b++*d+JNY*dK_YQ
^^^^^
|||||
|tabs
space


The spaces in the middle is actually a single tab character, but StackExchange renders it as four spaces.

Try it online!

• I count 64 bytes. – Conor O'Brien Apr 9 '16 at 0:34
• Because of the tab being shown as four spaces here. – Leaky Nun Apr 9 '16 at 0:35
• Definitely 64 bytes. mothereff.in/… – user31556 Apr 9 '16 at 2:26
• No, @KennyLau meant that the tab character should be placed instead of the four spaces. Look at the try it online link. – Mama Fun Roll Apr 9 '16 at 3:42
• @MamaFunRoll StackExchange automatically replaces tabs by 4 spaces. – orlp Apr 9 '16 at 9:33

# Python 2, 141 137 bytes

def S(s,l=[0]):
for c in s:l+=[l[-1]-(c in"aeiouAEIOU")+(c<"!")]
for h in sorted(set(l)):print"".join([" ",c][i==h]for i,c in zip(l,s))

• This one doesn't seem to descend on spaces – Score_Under Apr 9 '16 at 2:50
• @Score_Under It works fine on my machine. Are you testing on Python 2? – orlp Apr 9 '16 at 9:34
• It's working. I don't entirely know how but I must have made a mistake when pasting it the first time. – Score_Under Apr 9 '16 at 13:03

## JavaScript (Firefox 30-57), 151 bytes

s=>[...s].map((c,i)=>r[c<'!'?n++:/[AEIOU]/i.test(c)?n--:n][i]=c,n=s.length,r=[for(_ of s+s)[]])&&[for(a of r)if(s=[for(c of a)c||' '].join)s].join\n


Where \n represents the literal newline character.

• With template strings you can put a newline in a string, so you can replace /n with   – Generic User Apr 9 '16 at 0:32
• @GenericUser The byte count is adjusted assuming that you have already done that; I just didn't want to use a literal newline in my post. – Neil Apr 9 '16 at 9:14

# C, 180 bytes

char s[99];i,j,p[99],m,M;main(c){for(gets(s);c=s[i];j+=strchr("aeiou",c|32)!=0,j-=c<33,m>j?m=j:M<j?M=j:0)p[i++]=j;for(;m<=M;putchar(10),M--)for(i=0;c=s[i];)putchar(M^p[i++]?32:c);}


Ungolfed:

char s[99];i,j,p[99],m,M;
main(c){for(gets(s);c=s[i];
j+=strchr("aeiou",c|32)!=0,j-=c<33,m>j?m=j:M<j?M=j:0)
//move current height up or down, adjust minimum and maximum height
p[i++]=j;  //record height of character
for(;m<=M;putchar(10),M--)  //from maximum to minimum height
for(i=0;c=s[i];)putchar(M^p[i++]?32:c);}  //print only characters on this height


# Perl, 110 bytes (108 bytes script + 2 bytes flags)

$h=0;map{$h{$h}.=' 'x($p-$p{$h}).$_;$p{$h}=++$p;$h+=/[aeiou]/i-/\s/}split//;print for@h{sort{$b<=>$a}keys%h}  Run with perl -nl script.pl, input is on stdin, output is on stdout. ## Deobfuscated I've renamed the variables more sensibly, made the code use strict and use warnings compliant, and made explicit a lot of the magic perl does automatically. This is just run as perl script.pl, because it replicates the effects of the -nl flags inside the script. use strict; use warnings; use English; # The effect of -l in perl's flags$INPUT_RECORD_SEPARATOR = "\n";
$OUTPUT_RECORD_SEPARATOR = "\n"; # These variables are magicked into existence our$column = 0;
our %line_col = ();
our %lines = ();

# The implicit while-loop is the effect of -n in perl's flags
while (defined(my $line = <>)) { # The "chomp" is part of perl's -l flag too chomp$line;

# Here starts the actual script. "$h=0" turns into... our$height = 0;
for my $char (split '',$line) {
if (!exists $line_col{$height}) {
# Setting it to 0 is a bit of a white lie, but it might as well be 0.
# Perl would otherwise have called the value "undef", which is
# similar to 0 in numeric contexts.
$line_col{$height} = 0;
}

$lines{$height} .= ' ' x ($column -$line_col{$height});$lines{$height} .=$char;

$column++;$line_col{$height} =$column;

$height++ if$char =~ /[aeiou]/i;
$height-- if$char =~ /\s/;
}

# Sort line heights numerically descending (so the greatest is printed first)
my @heights = sort { $b<=>$a } keys %lines;

for my $line (@lines{ @heights }) { print$line;
}
}


# JavaScript (ES6), 133

s=>s.replace(/[^aeiou ]*(.?)/gi,(z,x,c)=>(q=o[r]||'',o[r]=q+=' '.repeat(c-q.length)+z,x<'!'?++r:r?--r:o=[,...o]),o=[],r=0)&&o.join



Less golfed

s=>(
s.replace(/[^aeiou ]*(.?)/gi,(z,x,c)=>(
q = o[r] || '',
o[r] = q += ' '.repeat(c - q.length) + z,
x == ' ' ? ++r : r ? --r : o = [,...o]
), o = [], r = 0),
o.join\n
)


Test

f=s=>s.replace(/[^aeiou ]*(.?)/gi,(z,x,c)=>(q=o[r]||'',o[r]=q+=' '.repeat(c-q.length)+z,x<'!'?++r:r?--r:o=[,...o]),o=[],r=0)&&o.join

function test() {
i=I.value
O.textContent=f(i)
}

test()
#I { width:90%}
<input id=I oninput='test()' value='Programming Puzzles And Code-Golf'>
<pre id=O>

## Haskell (within ANSI terminal), 75 bytes

("\27[2J"++).(h=<<)
h ' '="\27[B "
h c|elem c"aeiouAEIOU"=c:"\27[A"
h c=[c]


Usage example: putStr $("\27[2J"++).(h=<<)$ "bcdef ghijkl"

This uses ANSI escape codes to move the cursor up and down.

# C, 173 160 156 155 bytes

Edit: Borrowed idea of using strchr from @mIllIbyte to shave off 13 bytes

Edit2: Streamlined the min/max comparisons, -4 bytes

Edit3: c can have any value to begin with -> into main(c) instead, -1 byte

p,l,j,m;main(c){char b[99],*s=gets(b);for(;j<m+2;p?putchar(c?l?32:c:10):l<j?j=l:l>m?m=l:0,l-=c?(c&=223)&&c-9?!!strchr("AEIOU",c):-1:(p=s=b,l+j++))c=*s++;}


Ungolfed and explained:

/* declare and initialize these variables to int and 0 */
p,l,j,m;

/* declares main, but also int c */
main(c)
{

/* we can handle strings of length 98 (+1 for string-terminating 0) */
/* we declare and initialize s to point to the beginning of the input
string for the first pass through the for loop */
char b[99],*s=gets(b);

/* the for-loop actually contains nested loops, where the inner loops
behave differently depending on the outer loop parameter p as follows:
p attains the values false (0) and true (non-null pointer), in this order.

p == false:
the inner loop has the parameter s and passes through all the characters
in the string until the string is exhausted (*s == 0). l is the vertical
position of the current character relative to the first character
(l = 0), smaller number = higher up. The purpose here is simply to find
the range of vertical positions [j, m] present in the string. The
commands in execution order are:

-- loop over s --

// test does not do anything since j <= m by design
1. j < m+2

// puts current char in c and increments string counter
2. c = *s++

// ensures that j (m) equals the min (max) of the vertical positions (l)
encountered so far. At first step j = l = m = 0.
3. l<j?j=l:l>m?m=l:0

// c != 0, this updates the vertical position for the next character
// c = SPC or C = TAB -> lower (l increases by 1)
// c = "aeiouAEIOU" -> higher (l decreases by 1)
4a. l-=(c&=223)&&c-9?!!strchr("AEIOU",c):-1

-- loop over s ends --

// c == 0, this resets the string pointer s and puts p = true, and
//         thereby initiates the next phase of the algorithm
//         see rest of the explanation at p == true)
4b. p=s=b

p == true:
now there are two inner loops. The outer of these has the parameter j,
which ranges from the smallest vertical position+1 (the value of j after
the p == false pass) to the largest vertical position+1 (m+2 after the
p == true pass). The innermost loop has the parameter s and passes through
all characters in the string until the string is exhausted (*s == 0) just
as in the p == false inner loop. Here l is now the vertical position
relative to the current position j-1, so that l == 0 when a character is
at the current level. Such characters are printed as is, whereas
characters at other levels are replaced by space. The end-of-string
marker 0 outputs a newline. The commands in execution order are:

-- loop over j --

// at first step increments j to point to be one more than the
// current vertical position. At other steps moves the current position
// (j-1) one vertical position downwards. Also, at all steps, this
// biases the vertical position counter l to be zero at the current
// vertical position (j-1)
1. l=-j++

// compare j to stopping criteria, exit if j > m+1
2. j < m+2

-- loop over s --

// puts current char in c and increments string counter
3. c = *s++

// outputs character as follows:
// c == 0 (end of string), output newline
// c != 0 (middle of string)
//  l == 0 (character at current vertcial position), output c
//  l != 0 (character not at current vertical position), output space
4. putchar(c?l?32:c:10)

// c != 0, this updates the vertical position for the next character
// c = SPC or C = TAB -> lower (l increases by 1)
// c = "aeiouAEIOU" -> higher (l decreases by 1)
5a. l-=(c&=223)&&c-9?!!strchr("AEIOU",c):-1

-- loop over s ends --

// c == 0, this resets the string pointer s for next loop over s
//         algorithm (see rest of the explanation at p == true)
5b. p=s=b

-- loop over j ends --
*/

for(;
j<m+2;
p?putchar(c?l?32:c:10):
l<j?j=l:l>m?m=l:0,
l-=c?(c&=223)&&c-9?!!strchr("AEIOU",c):-1:
(p=s=b,l+j++))
c=*s++;
}