Fun Formatting Ones—Part 1: The Task


Summary: June Blender explains Doug Finke's multiplication and formatting trick. Today…the task.

Microsoft Scripting Guy, Ed Wilson, is here. Today, Honorary Scripting Guy, June Blender, examines a multiplication and formatting trick that Doug Finke posted on the PowerShell Facebook page. I've broken this article into two parts:

Part 1: The Task   Explains what Doug was trying to do and how to do it in Windows PowerShell.

Part 2: The Method   Explains the techniques that Doug used.

If you already understand The Task, you can wait for Part 2: The Method. Now, here's June…

Windows PowerShell MVP, Doug Finke, author of Windows PowerShell for Developers, is one of nicest and most clever people in the Windows PowerShell community. I'm a big fan, because I almost always learn something really new when I read Doug's work. You can contact Doug at:

Last week, Doug posted the following fun multiplication and formatting trick:

Image of command output

Many people "liked" the post (including me), but I realized that not everyone understood why it worked. If you do understand it, you might be able to use the techniques for slightly less fun things, like work. Let's tease it apart and learn all that Doug has to teach. This post explains what Doug was trying to do and how to do it in Windows PowerShell.

It looks like Doug had a bit of fun with the cool patterns of squares of numbers with 1s, and he wanted to create this table of products in Windows PowerShell.

1    x 1    = 1

11   x 1    = 11

11   x 11   = 121

111  x 111  = 12321

1111 x 1111 = 1234321

Each row of the table multiplies n digits of 1s and shows the product. The number of 1-digits increases with each row. To do this, he creates a loop that goes from 1 to 8 and prints that number of 1s and their product. It would look like:

  • Row 1: Print one 1. Then print the product of 1 x 1.
  • Row 2: Print two 1s. Then print the product of 11 x 11.
  • Row 8: Print eight 1s. Then print the product of 11111111 x 11111111.

To do this, you use a loop that starts at 1 and ends at your stopping point, which for Doug, is 8. This is a bit tricky, because you're working with strings, not numbers.

The essential part of this task is to print a specified number of 1s (twice). To do that, you use string multiplication, so let's start with that topic.

Multiplying strings

Most scripting and programming languages have operators that add and multiply numbers, for example:

PS C:\> 3 + 4

7 <yawn>

 

PS C:\> 3 * 4

12 <duh>

But Windows PowerShell lets you use the same + and * operators to add and multiply strings.

When you add strings, Windows PowerShell concatenates them (no spaces):

PS C:\> "Power" + "Shell"

PowerShell

When you multiply a string by a number n, Windows PowerShell copies the string n times and concatenates the results:

PS C:\> "Car" * 3

CarCarCar

String multiplication isn't commutative. Because the first operand determines the operation, the string must come first in a string multiplication expression. If an integer comes first, Windows PowerShell tries to do integer multiplication, which doesn't permit strings.

PS C:\> 3 * "Car"

Cannot convert value "Car" to type "System.Int32". Error: "Input string was not in a correct format."

At line:1 char:1

+ 3 * "Car"

+ ~~~~~~~~~

    + CategoryInfo          : InvalidArgument: (:) [], RuntimeException

    + FullyQualifiedErrorId : InvalidCastFromStringToInteger

String multiplication lets you do some cool things. This For loops starts with $i = 1 and counts up to $i = 5. In each pass, it multiplies the "x" string by the value $i:

PS C:\> for ($i = 1; $i -le 5; $i++) {"x" * $i}

x

xx

xxx

xxxx

xxxxx

This is a handy trick. In languages that don't allow string multiplication, like Python and Java, you need nested loops to do this.

Doug has fun with the string "1". Note that "1" is a string enclosed in quotation marks, not an integer. In Doug's statement, he multiplies the "1" string by $i where i goes from 1 to 8:

for ($i = 1; $i -le 8; $i++)

{

"1" * $i

}

 

1

11

111

1111

11111

111111

1111111

11111111

This creates the first set of 1s for Doug's display. But he needs two sets of 1s on each row, separated by an " x ". We'll use string addition to get it.

Adding Strings

To get the first part of Doug's display, we can use a loop that multiplies a number by a "1" string . But Doug's table requires two sets of 1s, separated by an " x ".

To add the " x ", we'll use the addition operator (+). It concatenates the first set of ones, the " x " string, and the second set of ones.

("1" * $i) + " x " + ("1" * $i)

When $i is 1, it produces the first row. When $i is 2, it produces the second row:

PS C:\ps-test> $i = 1

PS C:\ps-test> ("1" * $i) + " x " + ("1" * $i)

1 x 1

 

PS C:\ps-test> $i = 2

PS C:\ps-test> ("1" * $i) + " x " + ("1" * $i)

11 x 11

Now, let's use a loop that starts $i at 1 and goes to 8 (inclusive):

for ($i = 1; $i -le 8; $i++)

{

    "1" * $i + " x " + "1" * $i

}

1 x 1

11 x 11

111 x 111

1111 x 1111

11111 x 11111

111111 x 111111

1111111 x 1111111

11111111 x 11111111

Looking good!

The next part is " = " string, so let's add it:

for ($i = 1; $i -le 8; $i++)

{

    "1" * $i + " x " + "1" * $i  + " = "                      

}

 

1 x 1 =

11 x 11 =

111 x 111 =

1111 x 1111 =

11111 x 11111 =

111111 x 111111 =

1111111 x 1111111 =

11111111 x 11111111 =

The "1" * $i part of the equation is getting a bit repetitive (and error prone). Let's assign it to the $n variable. Then, we can replace the instances of "1" * $i with $n:

for ($i = 1; $i -le 8; $i++)

{

    $n = "1" * $i   #Assignment

   

    $n + " x " + $n  + " = "                       

}

1 x 1 =

11 x 11 =

111 x 111 =

1111 x 1111 =

11111 x 11111 =

111111 x 111111 =

1111111 x 1111111 =

11111111 x 11111111 =

Next, Doug wants to display the product of each equation. To get the product, he needs to multiply numbers, not strings. Let's see how he does it.

Invoke-Expression: Convert number strings to numbers

Doug wants to display the products of the numbers that he's printing:

11 * 1 = 11

11 * 11 = 121

111 * 111 = 12321

1111 * 1111 = 1234321

But, he was playing with strings of 1s, not the number 1. To convert the strings of numbers to numbers for the expression, he uses the Invoke-Expression cmdlet. Let's see how this works:

PS C:\> "2 * 3"    #This is a string.

"2 * 3"

 

PS C:\> Invoke-Expression -Command "2 * 3"  #This is a string.

PS C:\> 6

Now, let's try it with "1" strings:

PS C:\> "111 * 111"    #This is a string.

"111 * 111"

 

PS C:\> Invoke-Expression -Command "111 * 111"  #Still a string.

PS C:\> 12321

Be careful. To make this work, the value must be a single string, and all values and operators must be convertible to an arithmetic expression.

You can also pipe the number string to the Invoke-Expression cmdlet, which is what Doug did. And he used the iex alias of Invoke-Expression. The next three statements are equivalent, and they can be used interchangeably:

PS C:\> Invoke-Expression -Command "111 * 111"

PS C:\> 12321

 

PS C:\> "111 * 111" | Invoke-Expression

PS C:\> 12321

 

PS C:\> "111 * 111" | iex

PS C:\> 12321

This Invoke-Expression feature gives Doug the product for his "ones x ones = product" statement. Let's add the statement to the For loop block:

for ($i = 1; $i -le 8; $i++)

{

    $n = "1" * $i

   

    $n + " x " + $n  + " = " +

 

    ("$n * $n" | iex)

}

 

1 x 1 = 1

11 x 11 = 121

111 x 111 = 12321

1111 x 1111 = 1234321

11111 x 11111 = 123454321

111111 x 111111 = 12345654321

1111111 x 1111111 = 1234567654321

11111111 x 11111111 = 123456787654321

This completes the task. It's tricky, but it works. In tomorrow's post, we'll talk about the techniques that Doug uses, including an interesting loop and pretty formatting. Stay tuned!

We invite you to follow us on Twitter and Facebook. If you have any questions, send email to scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

June Blender, Honorary Scripting Guy

Comments (1)

  1. Dr. Strange Lover says:

    Whoa! How does this work?
    $c=8;1..$c | % {$c++;$n="1"*$_;"{0,8}x{0,8}={1,$c}"-f$n,("$n*$n"|iex)}

Skip to main content