14

I got this problem somewhere, and I want to know how to solve this using PHP. Given this text:

$str = ' PHP is a widely-used general-purpose server side scripting language '; 

How to echo the text vertically like the given below:

 g e n e w r s i a e d l r s P e - v c l H l p e r a P y u r i n - r p g i u p s t u s s o i i a e s d n g a d e e g e 

I will select the simpler and more elegant code as the answer.

2
  • 2
    why don't you use css to do so? stackoverflow.com/questions/1080792/… Commented May 28, 2012 at 12:44
  • Doing with CSS is great. But I want to know how this can be solved in PHP. Think like you're writing a command line script and need to output something, like graph :) Commented May 28, 2012 at 12:50

15 Answers 15

9
+100

As others have already demonstrated, array_map is able to do the flip over which is basically the main problem you need to solve. The rest is how you arrange the code. I think your version is very good because it's easy to understand.

If you're looking more to some other extreme, handle with care:

$str = 'PHP is a widely-used general-purpose server side scripting language'; $eol = "\n"; $turned = function($str) use ($eol) { $length = max(     array_map(         'strlen',         $lines = explode($eol, trim($str))     ) ); $each = function($a, $s) use ($length) {     $a[] = str_split(         sprintf("%' {$length}s", $s)     );     return $a; }; return implode(     $eol,     array_map(         function($v) {             return implode(' ', $v);         },         call_user_func_array(             'array_map',             array_reduce($lines, $each, array(NULL))         )     ) ); }; echo $turned($str), $eol; 

Gives you:

 g e n e w r s i a e d l r s P e - v c l H l p e r a P y u r i n - r p g i u p s t u s s o i i a e s d n g a d e e g e 

This fixes the output of the other answer, which was incorrect (now fixed).

Sign up to request clarification or add additional context in comments.

2 Comments

Absolutely. Well, I have some other piece of code in another answer for that contest, let me pick it: stackoverflow.com/a/7891640/367456
@hakre so at least you don't need to uglify your code anymore - you just write minified code on the fly :) (i've found some unneeded spaces in it tho)
6

The code below will print $str vertically.

$lines = preg_split("/\r\n/", trim($str)); $nl = count($lines); $len = max(array_map('strlen', $lines)); foreach ($lines as $k => $line) { $lines[$k] = str_pad($line, $len, ' ', STR_PAD_LEFT); } for ($i = 0; $i < $len; $i++) { for ($j = 0; $j < $nl; $j++) { echo $lines[$j][$i].($j == $nl-1 ? "\n" : " "); } } 

3 Comments

how about $len = max(array_map('strlen', $lines); instead of the first for loop?
@allen213: Wow great! I never thought about it. I'm using it in my code. I hope you don't mind :)
Not mine saw it before here > stackoverflow.com/questions/1762191/…
3

I took some of the code from @bsdnoobz and simplified it. I'm using \n as a line break because I work on a Mac and \r\n doean work here.

$lines = preg_split("/\n/", trim($str)); $len = max(array_map('strlen', $lines)); $rows = array_fill(0,$len,''); foreach ($lines as $k => $line) { foreach (str_split(str_pad($line, $len, ' ', STR_PAD_LEFT)) as $row => $char){ $rows[$row] .= $char; } } echo implode("\n",$rows)."\n"; 

1 Comment

that's why PHP got PHP_EOL
3
$a = explode(PHP_EOL, trim($str)); $h = max(array_map('strlen', $a)); $w = count($a); $m = array_map('str_split', array_map('sprintf', array_fill(0, $w, "%{$h}s"), $a)); $t = call_user_func_array('array_map', array_merge(array(null), $m)); echo implode(PHP_EOL, array_map('implode', array_fill(0, $h, ' '), $t)), PHP_EOL; 

PHP_EOL should be replaced with "\n", "\r\n" or '<br/>' where appropriate. The whole code after the first line could easily become one big expression, only its readability would suffer a little bit ;-)

$a is the array of lines, $h is the final height, $w is the final width, $m is the matrix of characters (after padding the strings), $t is the transposed matrix.

The array_fill(0, $h, ' '), on the last line can simply be omitted if the space between columns is not needed. On the other hand, not printing trailing space characters can be achieved like this:

echo implode(PHP_EOL, array_map('rtrim', array_map('implode', array_fill(0, $h, ' '), $t))), PHP_EOL; 

I have taken the question as an excercise in avoiding explicit loops (which are usually more expensive than loops inside PHP functions, although in this case the advantage could be lost). The important trick here is the matrix transposition, which I have taken from this answer by Codler

Anyhow, the complexity of the whole algorithm is O(width × height), just like the one of most algorithms on this page, except for those that repeatedly concatenate strings in order to obtain the lines, whose complexity is O(width² × height)

Comments

1

I'll give this a shot:

$arrlines = explode(PHP_EOL, trim($str)); $max = max(array_map('strlen', $arrlines)); foreach($arrlines as $val) $arrlines = str_pad($val,$max," ",STR_PAD_LEFT); for($x=0;$x<$max;$x++){ for($y=0;$y<count($arrlines);$y++) $string .= strlen(trim($arrlines[$y][$x])) > 0 ? $arrlines[$y][$x]."&nbsp;":"&nbsp;&nbsp;"; $string .= "\n"; } echo '<pre>'.$string.'</pre>'; 

Line numbers:

  1. save the string per line in an array $arrlines separated by the PHP constant PHP_EOL
  2. get the maximum string length and save it to $max
  3. loop for every element of $arrlines, then
  4. add padding to the left of the string to make its string length equal to $max

for lines 5-9, nested loop to save the $arrline[$y][$x] to $string, and line 10 outputs the result.

Result can be found here.

Comments

0

Here is simple code any one can understand this code

 $str = ' PHP is a widely-used general-purpose server side scripting language '; $lines = array(); $sentences = explode("\n\n",$str); foreach($sentences as $sentence){ if($sentence != ""){ $each_lines = explode("\n",$sentence); foreach($each_lines as $each_line){ if($each_line != ""){ $lines[] = $each_line; } } $lines[] = "\t"; } } $before_sort = $lines; usort($lines, "cmp_len"); $big_length = strlen($lines[0]); foreach($before_sort as $key=>$arr){ $this_length = strlen($arr); if($this_length < $big_length){ $no_of_letters = $big_length - $this_length; $text = ""; for($i=0;$i<$no_of_letters;$i++){ $text .= " "; } $text .= $before_sort[$key]; $before_sort[$key] = $text; } } $temp = array(); echo "<table>"; for($i=0;$i<$big_length;$i++){ echo "<tr>"; foreach($before_sort as $arr){ echo "<td>"; echo str_replace("\t","&nbsp;&nbsp;&nbsp;&nbsp;",$arr[$i]); echo "</td>"; } echo "</tr>"; } echo "</table>"; function cmp_len($a, $b){ return (strlen($a) < strlen($b)); } 

1 Comment

It is unnecessary to sort all of the lines by length when you only need the max value. PHP8 is barking at your sorting return type. 3v4l.org/Zm95k
0

Check this answer:

<?php $str = 'PHP is a widely-used general-purpose server side scripting language'; function getVerticalString($str){ $str = preg_split('/\r|\n/',$str); $maxlength = 0; $totalstr = count($str); for($i=0;$i<$totalstr;$i++){ if($maxlength<strlen($str[$i])){ $maxlength = strlen($str[$i]); } } $strings = array(); for($i=0;$i<$maxlength;$i++){ $strings[$i] = ""; for($j=0;$j<$totalstr;$j++){ $temp = strlen($str[$j])-($maxlength-$i); if($temp>=0){ $strings[$i] .= substr($str[$j],$temp,1); } else{ $strings[$i] .= " "; } } } return implode("\r",$strings); } echo "<pre>".getVerticalString($str)."</pre>"; 

this outputs:

 g e n e w rs i ae d lr s Pe -v cl Hl pe ra Py ur in - r pg iu ps tu ss oi ia e sd ng ad ee ge 

As per your requirements. :)

1 Comment

I recommend a splitting pattern of /\R/.
0

I wouldn't really call it shorter, but line count is less ;-)

$nr_lines = count($lines = preg_split('/\r?\n/', trim($str))); $maxlen = max($lengths = array_map('strlen', $lines)); // get line lengthts and maximum $line_ptrs = array_fill(0, $nr_lines, 0); // last character position per line for ($j = 0; $j != $maxlen; ++$j) { for ($i = 0; $i != $nr_lines; ++$i) { // $maxlen - $lengths[$i] indicates where printing start for this line echo $j >= $maxlen - $lengths[$i] ? $lines[$i][$line_ptrs[$i]++] : ' '; } echo "\n"; } 

It does the padding and printing in the same inner loop, aided by the $line_ptrs and $lengths array to keep track of which character to print next.

Benchmark

Based on 10,000 iterations, this code performs 19% better than the first answer.

1 Comment

I recommend a pattern of /\R/ here.
0

Please note that the length of a string and the number of elements in an array are cached by PHP per variable. Calling strlen or count repeatedly has only the performance penalty of an empty function call (on subsequent calls, the length or count are not measured twice internally by PHP).

This works with a Mac (\r), Linux (\n) or Windows (\r\n) input string, and/or with an empty input string, and/or with a one line input string, and outputs with a prefered line delimiter (default is PHP_EOL):

function matrix_vertical_align_bottom($str, $strEOL=PHP_EOL) { $arrLines=explode("\n", $str); $nMaxLength=max(array_map("strlen", $arrLines)); $nRotatedWidth=count($arrLines)+strlen($strEOL); //allocate once $strRotated=str_pad( "", $nRotatedWidth*$nMaxLength-strlen($strEOL), str_repeat(" ", count($arrLines)).$strEOL ); foreach($arrLines as $nRotatedColumnIndex=>$strLine) { $nRotatedVerticalPadding=$nMaxLength-strlen($strLine); for($nColumnIndex=strlen($strLine)-1; $nColumnIndex>=0; $nColumnIndex--) $strRotated[$nRotatedWidth*($nColumnIndex+$nRotatedVerticalPadding)+$nRotatedColumnIndex]=$strLine[$nColumnIndex]; } return $strRotated; } echo matrix_vertical_align_bottom(preg_replace("/(\r\n|\r|\n)/", "\n", trim($str))); 

The performance is pretty good, the above algorithm is just translating coordinates while rotating the string by 90 degrees. There is no memory re-allocation because of string expanding provoked by padding individual lines (for input strings with lots of lines, this would be a performance hit).

HTML output is not assumed, nl2br(htmlspecialchars( )) when outputting should do the trick + a mono spaced font.

Comments

0

It's like the old saying goes:

If it can be done without regex, try it anyway.

Some people, when confronted with a problem, think “I know, I'll use regular expressions.” Now they have zero problems.

"I took the [road] less traveled by" - Robert Frost

With so many similar techniques on this page, I thought I'd offer a fresh perspective. Here is a technique by which the input string is consumed within a loop and the last character (if it exists) of each line is added to the output array as a space-delimited string. The loop is broken when the entire input string consists of whitespace characters. I did not benchmark it, but I would be curious to hear any comparisons if anyone is inclined to provide their findings.

Code: (Demo)

$result = []; while (!ctype_space($string)) { $line = []; $string = preg_replace_callback( "~.$|^$~m", function($m) use(&$line) { $line[] = $m[0] === '' ? ' ' : $m[0]; return ''; }, $string, ); array_unshift($result, implode(' ', $line)); } echo implode("\n", $result); 

Comments

0

My function to rotate text without any language construct loops:

/** * Rotates text * * @param string $str String to rotate * * @return string Rotated string */ function rotateText($str) { $lines = explode(PHP_EOL, $str); $lengths = array_map('strlen', $lines); $maxLength = max($lengths); $lines = array_map(function ($line) use ($maxLength) { return str_pad($line, $maxLength, ' ', STR_PAD_LEFT); }, $lines); $chars = array_map('str_split', $lines); array_unshift($chars, null); $rotatedLines = call_user_func_array('array_map', $chars); $rotatedText = join(PHP_EOL, array_map('join', $rotatedLines)); return $rotatedText; } echo "<pre>", rotateText($str), "</pre>"; 

2 Comments

array_map('str_split', $lines); //double cycle + array with lots of elements (and you are using array_map 5 times, thats at least 5 cycles). array_map('join', $rotatedLines) would also be lots of cycles (join each array into an array of strings, then join again).
Yes, agree. Your solution is about 40% faster than my one.
-1

Here's my shot at it. Couldn't figure out how to do it without the nested loop.

$lines = array_map('strrev', explode("\r\n", trim($str))); $new = array(); foreach(range(0, max(array_map('strlen', $lines))) as $i){ foreach($lines as $line){ $new[$i] .= (!empty($line[$i]) ? $line[$i] . ' ' : ' '); } } echo implode("\r\n", (array_slice(array_reverse($new), 1))); 

3 Comments

This looks promising. Unfortunately the result is 95% matches. There are extra whitespaces in the output string.
Yeah, you're right. Didn't notice the end row had no extra space. And looks like I missed the white space on line sixteen. I'll try again later.
This answer appears to be very broken. 3v4l.org/nGbqv
-1

Try this one,

$str = 'PHP is a widely-used general-purpose server side scripting language'; $strArr = explode("\r\n", $str); $max =max(array_map('strlen', $strArr)); for($i=0; $i< $max;$i++) { for($x=0;$x < count($strArr); $x++) { $strVal = $strArr[$x]; $y = $i -($max - strlen($strVal)); $vertical .= strlen(trim($strVal[$y]))<> 0 ? $strVal[$y]." " : " "; } $vertical .="\n"; } echo "<pre>".$vertical; 

1 Comment

This answer appears to be very broken. 3v4l.org/2Y8b5
-1

The best and simplest way :)

<style type="text/css"> #heading{ /* vertical text css */ width:1em; text-transform:uppercase; } </style> <h1 align="center" id="heading">h e l l o</h1> 

DEMO

1 Comment

btw, it only prints a single line, while I need multiple lines :P
-1
$a= 'HelloWorld!'; $b=strlen($a); for($i=0;$i<$b;$i++){ echo $c=substr($a,$i,1).'<br>'; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.