53
\$\begingroup\$

What general tips do you have for golfing in Windows PowerShell? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to PowerShell (e.g. "remove comments" is not an answer). Please post one tip per answer.

—taken nearly verbatim from marcog's question.

\$\endgroup\$
1
  • 6
    \$\begingroup\$ When I googled "PowerShell golf" this was the first hit! \$\endgroup\$
    – Matt
    Commented Mar 14, 2016 at 15:53

51 Answers 51

29
\$\begingroup\$

Powers of 10 literals with scientific notation:

4e6 = 4000000

Powers of 2 literals:

4KB = 4096
4MB = 4194304
4GB = 4294967296

# TB and PB suffixes also exist, but less useful for golf.

Could come in handy.

\$\endgroup\$
24
\$\begingroup\$

If you need to run a loop, and you know exactly how many times it needs to run every time, consider piping an array of contiguous integers into ForEach-Object via the % alias instead of using for.

for($x=1;$x-le10;$x++){...}

vs

1..10|%{...}

\$\endgroup\$
20
\$\begingroup\$

You can skip spaces a lot in PowerShell. If it feels like it might not be needed, it quite possibly isn't. This is particularly useful in comparisons.

Example:

$x-eq$i-and$x-ne9

vs.

$x -eq $i -and $x -ne 9

If you need to branch your script based on the result of a single test which may have multiple outcomes, switch can sometimes match or beat an if statement.

Example (switch vs. if/else - tie):

if($x%2-eq0){'even'}else{'odd'}

vs.

switch($x%2){0{'even'}1{'odd'}}

Or (switch vs. if/elseif/else - switch wins by 15):

if($x%2-eq0){'even'}elseif($x%2-eq1){'odd'}else{'error'}

vs.

switch($x%2){0{'even'}1{'odd'}2{'error'}}

If the switch is actually based on certain math results, like the modulo operation above, you can replace the switch entirely with an array. Here, it saves another 13 characters and is even shorter than the original two-option if/else statement. (Thanks to Danko Durbic for this bit.)

('even','odd','error')[$x%2]

If you will be using a particular command a lot, especially one without a pre-existing short-hand alias, set up a single-character alias early on.

Example:

nal g Get-Random;g 10;g 10;g 10

vs.

Get-Random 10;Get-Random 10;Get-Random 10
\$\endgroup\$
1
  • 2
    \$\begingroup\$ ('even','odd')[$x%2] FYI. \$\endgroup\$ Commented Nov 16, 2018 at 2:09
16
\$\begingroup\$

Encapsulating the command that defines a variable in parenthesis allows you to feed the variable's definition directly to other commands.

For example, you can set $x and then set $y based on the value of $x in one shot with this:

$y=($x=1)+1

Instead of this:

$x=1;$y=$x+1

You can set $h and output it with this:

($h='Hello World!')

Instead of this:

$h='Hello World!';$h
\$\endgroup\$
1
  • 1
    \$\begingroup\$ This is especially useful for calling methods on objects. ($x=New-Object Windows.Forms.Form).Controls.Add($t) \$\endgroup\$
    – SpellingD
    Commented Nov 26, 2013 at 21:49
15
\$\begingroup\$

A switch can act like a loop, when given an array. For example:

$FooBarMeh='a','b','c'
switch ($FooBarMeh)
{
    'a'{'FOO'}
    'b'{'BAR'}
    default{'MEH'}
}

Will output:

FOO
BAR
MEH

I'm not totally sure where this will be useful, but I expect it will be handy for someone some time.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ It is handy every time you need a ForEach-Object and a switch within that. It's for example (in real-world) code very nice for writing quick parsers of text files where you need to do different things depending on which regex a line matches. \$\endgroup\$
    – Joey
    Commented Nov 24, 2013 at 12:02
  • \$\begingroup\$ This is... damn weird. An array shouldn't match with a char, but it does. \$\endgroup\$
    – cat
    Commented Apr 24, 2016 at 14:56
  • \$\begingroup\$ @Joey That's what switch -File $path is for \$\endgroup\$ Commented Nov 16, 2018 at 2:10
  • \$\begingroup\$ Not everything that's parsed happens to be a file. \$\endgroup\$
    – Joey
    Commented Nov 17, 2018 at 15:17
14
\$\begingroup\$

Want to find the maximum or minimum of a collection of values? Tried

(...|measure -ma).Maximum

or

(...|measure -mi).Minimum

already?

Just sort and use the last or first item:

(...|sort)[-1]  # maximum
(...|sort)[0]   # minimum
\$\endgroup\$
3
  • 1
    \$\begingroup\$ And, if you know the length of the collection of values and it's less than 10... \$\endgroup\$
    – wizzwizz4
    Commented Jan 10, 2016 at 14:12
  • \$\begingroup\$ @wizzwizz4: Oh, maximum lengths can often be employed creatively, not necessarily being 10. Although for this particular case I don't recall an instance where it ever helped. \$\endgroup\$
    – Joey
    Commented Jan 10, 2016 at 14:53
  • 1
    \$\begingroup\$ I mean, if the final item is known to be 0, 1, 2, 3, 4, 5, 6, 7, 8 or 9, it would save a byte to write the known length instead of -1. \$\endgroup\$
    – wizzwizz4
    Commented Jan 10, 2016 at 14:55
14
\$\begingroup\$

Replace [math]::pow with multiplication. Instead of

[math]::pow($a,$b)

you can write

"$a*"*$b+1|iex

This works for integer exponents >= 0.

\$\endgroup\$
1
  • \$\begingroup\$ I needed to see this answer to understand iex ... Alias for Invoke-Expression \$\endgroup\$ Commented Jan 17, 2019 at 16:57
14
\$\begingroup\$

Shortening Property Names

Sadly, unlike parameters, properties/methods (anything accessed with a dot .) cannot usually be shortened down to its unambiguous form.

But certain cmdlets can operate on property names and take wildcards, and there are little-known parameter sets of % and ? that can be useful.

Usually we pass in a scriptblock and refer to the item with $_, but there's another form of these that takes a property name, and it accepts a wildcard.

$o|select Le*  
$o|%{$_.Length}

With a property like .Length we can't use the v3 magic that would normally work on an array because Length is a property of the array itself, so the above two could be used to get the lengths of the individual members. The select comes in a little bit shorter.

But % can take a property name directly and return that value:

$a|% Length

Which can be shortened with wildcards. The wildcard must resolve to a single property (or method, more on that later), so it will throw a helpful error if it doesn't, indicating exactly which members it could resolve to.

In the case of Length, Le* is typically the shortest. Even on a single string, this method is 1 byte shorter than just using the property.

$a.Length                # 9   #(doesn't work on array)
$a|%{$_.Length}          # 15
$a|% Le*                 # 8

But depending on what you're doing with this, this can be worse. You can do $a.Length*5 but to do it with the pipeline expression you'd have to wrap it ($a|% Le*)*5; might still be worth it if it's against an array, but the point is it's not always appropriate as a straight substitution.

It works with methods too, and you can leave off the () which makes a full name the same length, but same restriction as above about sometimes having to wrap it. The method must have an overload that takes no parameters (you can pass arguments by placing them after the method name, which is really nice):

$a.ToUpper()             # 12
$a|% *per                #  9

With arguments:

'gaga'-replace'g','r'    # 21
'gaga'|% *ce g r         # 16

These aren't strictly the same in that the -replace operator does a regex replace, but if you're just doing a string replace, it can (now) be shorter to use the method; it helps that the strings are cmdlet arguments instead of method arguments so they don't need to be quoted.

Where-Object Properties

? can take (partial) property names as well, and apply an "operator" to it (in the form of switch parameters). Again this can be shorter than using the standard Where-Object scriptblock approach if the property name is sufficiently long and unique.

$a|?{$_.Length-gt5}      # 19
$a|? Le* -GT 5           # 14

($a|% Le*)-gt5           # 14 - Lengths, not objs
\$\endgroup\$
9
  • 1
    \$\begingroup\$ One of the few places where whitespace is required. Nice find, though. I can think of several areas where this will shorten things. \$\endgroup\$ Commented Feb 27, 2017 at 15:48
  • \$\begingroup\$ Found you a way to get it shorter ;-) ... the sad thing about this is that as a whole it's a pipeline again, which limits its usefulness a bit. Or rather, there are a lot of places where you'd have to wrap it into parentheses again to get an expression. But few golf techniques are without trade-offs ... \$\endgroup\$
    – Joey
    Commented Feb 27, 2017 at 15:56
  • \$\begingroup\$ @Joey yeah I mentioned that, it really does depend. If you're passing it along in the pipeline or just returning it it's great, otherwise it may need wrapping; even with wrapping it could be shorter in certain situations. \$\endgroup\$
    – briantist
    Commented Feb 27, 2017 at 15:57
  • 1
    \$\begingroup\$ @ConnorLSW ah yes I meant to update this as I'd since realized you can pass args that way, which greatly ups its usefulness. Great use of .ToString()! \$\endgroup\$
    – briantist
    Commented Apr 3, 2017 at 21:29
  • 2
    \$\begingroup\$ @briantist this has turned PowerShell into a much more competitive language, really cuts out some of the annoyingly verbose .Net calls. \$\endgroup\$
    – colsw
    Commented Apr 4, 2017 at 8:19
13
\$\begingroup\$

Comparison operators work on collections of values by returning matching values:

1..5 -gt 2

will yield 3, 4 and 5. In some cases this can help to save an otherwise longer |?{$_...}.

-match is a comparison operator too.

\$\endgroup\$
2
  • 3
    \$\begingroup\$ Note that you don't need spaces in that example 1..5-gt2 \$\endgroup\$
    – Matt
    Commented Mar 14, 2016 at 15:34
  • 1
    \$\begingroup\$ I know; this was more about showing the technique. Spaces are in a separate answer. \$\endgroup\$
    – Joey
    Commented Mar 14, 2016 at 18:08
13
\$\begingroup\$

Use aliases whenever possible. There are a bunch of useful ones:

?        Where-Object
%        ForEach-Object
gu       Get-Unique
sort     Sort-Object
iex      Invoke-Expression
\$\endgroup\$
11
\$\begingroup\$

Finding a sum the long way:

(...|measure -s).Sum

A shorter way:

...|%{$s+=$_};$s

And even shorter:

...-join'+'|iex
\$\endgroup\$
10
\$\begingroup\$

for loops can have anything between 0 and three statements in their header:

Endless loop:

for(){}

Loop with initialization:

for($n=0){}

Loop with initialization and end condition:

for($n=0;$n-lt7){}

In such cases the additional semicolons at the end may be omitted (it's explicitly stated in the language specification, so it's not an implementation detail) in contrast to C-like languages which always require exactly three statements.

This also makes while a bit shorter. Compare

while(...){}

and

for(;...){}

With the added bonus that you can stick in a previous line (if there is one) into the for as well without extra cost (and even saving a character).

\$\endgroup\$
9
\$\begingroup\$

Semicolons and line breaks are interchangeable. Golfed code is often more readable if not jammed into a single line. And the length is still the same (provided you use U+000A as line break which PowerShell handles without problems).

\$\endgroup\$
8
  • 2
    \$\begingroup\$ Wait, we are worried about readability?! \$\endgroup\$
    – Kevin Cox
    Commented Nov 22, 2013 at 15:50
  • 4
    \$\begingroup\$ If it has no effect on the length ... why not? \$\endgroup\$
    – Joey
    Commented Nov 22, 2013 at 17:02
  • \$\begingroup\$ Doesn't it technically make a difference since a semicolon is one less character than \r\n? Only in Unix it is a singular \n. \$\endgroup\$ Commented Jan 9, 2014 at 4:47
  • 2
    \$\begingroup\$ @Vasili: You can save the files just fine with only U+000A between the lines. PowerShell won't complain. Which I wrote in the answer already, by the way. Line breaks are a property of the file, not of the operating system it is used on. No one says that you cannot use Unix line endings on Windows. \$\endgroup\$
    – Joey
    Commented Jan 9, 2014 at 5:50
  • \$\begingroup\$ @Joey If you’ve used a fresh install of windows to do some quick programming, you’ll notice that Notepad doesn’t play nice with \x0a \$\endgroup\$
    – Stan Strum
    Commented Dec 18, 2018 at 20:58
9
\$\begingroup\$

The Get verb is implied. This can shorten any Get-Frob to just Frob. Frequent contenders are date or random.

Note that this won't work properly in some cases because you might have GNU utilities in your path (or other native programs that clash). Order of command lookup in that case seems to prefer the native program before it considers cmdlets with the Get- removed:

PS Home:\> date

Freitag, 15. November 2013 07:13:45


PS Home:\> $Env:Path += ';D:\Users\Joey\Apps\GnuWin32\bin'
PS Home:\> date
Fr Nov 15 07:14:13 W. Europe Standard Time 2013
\$\endgroup\$
7
  • \$\begingroup\$ This doesn't quite always work out as expected. I have a script that starts with nal g Get-Random to save characters later on. Changing it to nal g Random causes the script to hang indefinitely (or, at least, take an inordinate amount of time to process - I haven't had the patience to wait for it to end, but it's taking several orders of magnitude longer than the original form before I abort). \$\endgroup\$
    – Iszi
    Commented Nov 15, 2013 at 0:56
  • 2
    \$\begingroup\$ Two short Measure-Command invocations (100 times Get-Random vs. random) tell me it's about 500 times slower. I didn't know that before, to be honest. But it's good to keep it in mind, especially in loops with many iterations. That being said, golfed code should be short, not fast (that being said, it sucks to have to wait two days for an answer to a Project Euler problem). \$\endgroup\$
    – Joey
    Commented Nov 15, 2013 at 6:18
  • 1
    \$\begingroup\$ My problem was running 10,000 iterations of the Monty Hall scenario each requiring three iterations of Get-Random. A 50,000% increase in processing time, multiplied across 30,000 runs is pretty nasty. \$\endgroup\$
    – Iszi
    Commented Nov 15, 2013 at 6:23
  • \$\begingroup\$ Wow. It looks like the same probably holds true for any alias. Tested Get-Variable vs. gv and got a similar comparison. \$\endgroup\$
    – Iszi
    Commented Nov 15, 2013 at 14:43
  • 1
    \$\begingroup\$ That could be it. I did some math and figured my Monty Hall script should take about 1.5 hours (normally, 10-11 seconds) to run without Get-. That's a pretty gnarly bloat in run time for a savings of just 4 characters in length. \$\endgroup\$
    – Iszi
    Commented Nov 15, 2013 at 14:49
9
\$\begingroup\$

Casting to string:

[string]$x

vs.

"$x"

Casting to string like this can also be used to flatten an array of strings, instead of joining it:

$a = @('a','b','c')
$a -join ' '

vs.

$a = @('a','b','c')
"$a"

Casting a string to a numeric type:

[int]$x     [float]$x

vs.

+$x

Also very useful to know that PowerShell always takes the type of the left operand to determine the final type of an expression and conversions to apply:

'1'+2    -> '12'
1+'2'    -> 3

which can help determining where needless casts are.

\$\endgroup\$
8
\$\begingroup\$

If you are assigning an array that you know will only have two values, don't use indexing.

Something like this:

$a="apple","orange"
$a[0] # apple
$a[1] # orange

Can easily be turned into this:

$a,$o="apple","orange"
$a # apple
$o # orange

This can also be useful for if you just need to the first element of an array:

$a,$b=1..10
$a # 1
$b # 2..10
\$\endgroup\$
1
  • \$\begingroup\$ In this case (with the strings) $a="apple";$o="orange" is identical in length. It's the longer arrays that can sometimes be optimised fairly nicely, e.g. by putting all elements in a string with a separator and then using -split (best to use whitespace as separator because then the unary -split will suffice). \$\endgroup\$
    – Joey
    Commented Jun 2, 2014 at 6:03
7
\$\begingroup\$

Use the $ofs special variable to change the Output Field Separator used when stringifying an array. Useful if you're needing to transform arrays to strings multiple times.

For example:

$a=1,2,3,4
$a-join',';$a-join','
$ofs=',';"$a";"$a"

Saves 2+n characters on the 2nd -join, where n is the length of the separator, and saves an additional 5+n for the 3rd -join and each thereafter.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Sadly very rarely useful (at least in my golfing so far – I tend to strip it down to only a single join at the end). \$\endgroup\$
    – Joey
    Commented Mar 4, 2016 at 20:07
7
\$\begingroup\$

When using a number as an argument to an operator that would otherwise require a string, you can use the number directly. Compare

...-join'0'

vs.

...-join0

Works with -split as well. The argument is always converted to a string first.

\$\endgroup\$
2
  • \$\begingroup\$ For reference, this works with -replace as well. \$\endgroup\$ Commented Oct 24, 2017 at 13:23
  • \$\begingroup\$ -replace also works with no second argument wheb you want to remove matches, \$\endgroup\$
    – Joey
    Commented Oct 25, 2017 at 9:05
6
\$\begingroup\$

Don't forget that you don't always need to provide the full name of a parameter, and some parameters are positional.

Get-Random -InputObject (0..10)

...can be trimmed to...

Get-Random -I (0..10)

...because "I", in this case, is enough to uniquely identify InputObject from the other valid parameters for this command.

You could trim it further to...

Get-Random (0..10)

...because InputObject is a positional parameter.

Piping is usually shorter than feeding objects as a parameter, especially when it can remove the need for parenthesis. Let's trim our random number generator further...

0..10|Get-Random

Also be on the lookout for other ways to accomplish the same thing, even if you can't change the command. For the above case, you could do this:

Get-Random 11

Or, incorporating another suggestion*:

Random 11

**Note: Omitting Get- from a command name can bloat the run time by about 50,000%. Not bad if you only need the command once, but be careful using it in long loops.*

And that's how can knock a simple command down to a third of its size.

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

Automatic variables have booleans for True and False as $true and $false but you can get similar results using the logical not operator ! and the integers 0 and 1( or any non-zero integer.)

PS C:\Users\matt> !1
False

PS C:\Users\matt> !0
True

Near all PowerShell expressions can be evaluated as booleans. So as long as you are aware of how certain data is evaluated you can get booleans and never need to explicitly cast them. Be aware of the LHS value when doing these.

  • Integer 0 is false and non-zero integers are evaluated to true.
  • non-zero length strings are true and empty or null (and nulls themselves) strings are false.

There are other examples but you can easily test by doing a cast

PS C:\Users\matt> [bool]@(0)
False
\$\endgroup\$
4
  • 1
    \$\begingroup\$ Many other data types also have similar status. Uninitialized variables default to $null, which is falsey. The empty string (and variables set to the empty string) are falsey. Etc. This can then be used to shortcut indexing into an array (e.g., when doing an if/else), as was used in FizzBuzz. \$\endgroup\$ Commented Mar 14, 2016 at 16:45
  • \$\begingroup\$ @TimmyD Very true. Using ints is just shorter \$\endgroup\$
    – Matt
    Commented Mar 14, 2016 at 16:53
  • \$\begingroup\$ @TimmyD I wanted to answer fizzbuzz until I saw yours.... Can't beat that .... at least not yet \$\endgroup\$
    – Matt
    Commented Mar 14, 2016 at 16:55
  • \$\begingroup\$ Note that in many cases you don't actually need a bool. You can use 0 and 1 just as well. Implicit conversions help a lot in that regard (which you can often force with certain operators). \$\endgroup\$
    – Joey
    Commented Mar 14, 2016 at 18:09
6
\$\begingroup\$

Fake ternary operator. You can assign straight from an if statement:

$z=if($x-eq$y){"truth"}else{"false"}

But you can use a 2-element array and use the test to index into it. $falsey results get element 0, $truthy results take element 1:

$z=("false","true")[$x-eq$y]

NB. that this is really doing array indexing, and if the test results in a value which can be cast to an integer, you'll ask for an item outside the bounds of the array and get $null back, and will need to do !(test) to force cast the result to a bool, with the options reversed.

[ Edit: There's a genuine ternary operator in PowerShell 7+ in the C-language-style cond ? 1 : 0 ]

\$\endgroup\$
2
  • \$\begingroup\$ The first snippet didn't work at one point in time (older PowerShell versions), if I remember correctly, making it necessary to wrap it into $(). There was essentially a distinction of using pipelines made from commands and statements as expressions. Seems that is gone by now, at least in PowerShell 5. \$\endgroup\$
    – Joey
    Commented May 14, 2016 at 8:47
  • \$\begingroup\$ Additionally, if the array elements are non-static, both get parsed and processed as part of the array creation before the indexing happens and the result is assigned. For example. \$\endgroup\$ Commented May 25, 2016 at 20:12
6
\$\begingroup\$

Use [bigint] instead of [Math]::floor

The bigint datatype is a numeric type that can store arbitrarily large numbers and holds no decimal values. It always floors when using [bigint]::new().

A suffix for it was added in PowerShell core but using the n prefix rounds normally instead of floor which doesn't work for this.

Example:

[bigint](.9)        # 0
[bigint]3/4         # 0
[bigint](.1)        # 0
[bigint]1/4         # 0
[bigint]10.5        # 10
[bigint]100/3       # 33

(.9)n               # 1
3/4n                # 1
(.1)n               # 0
1/4n                # 0
10.5n               # 11
100/3n              # 33
\$\endgroup\$
4
  • \$\begingroup\$ only from PS7+. does not work with TIO yet. because TIO use PS6. docs.microsoft.com/en-us/powershell/module/… \$\endgroup\$
    – mazzy
    Commented Apr 21, 2021 at 18:08
  • 1
    \$\begingroup\$ @mazzy ps5 works for the first method \$\endgroup\$ Commented Apr 21, 2021 at 18:30
  • 1
    \$\begingroup\$ yes, it is. $x-$x%1 is better Try it online! :) \$\endgroup\$
    – mazzy
    Commented Apr 21, 2021 at 18:40
  • 1
    \$\begingroup\$ @mazzy that saves bytes when you have defined variables, if you are doing a one-time computation you don't need variables and it saves 3 bytes. \$\endgroup\$ Commented Apr 21, 2021 at 18:47
5
\$\begingroup\$

Absolute value

With

$n=-123

Instead of

[math]::abs($n)

use

$n-replace'-'

Of course, the savings are cancelled if parentheses are needed.

\$\endgroup\$
1
  • \$\begingroup\$ Or if you need the result on the left side of an operator ;-) \$\endgroup\$
    – Joey
    Commented Nov 15, 2013 at 16:44
5
\$\begingroup\$

If you need to silence errors, the obvious variant would be

try{ <# something that throws errors #> }catch{}

However, this is way too long. A shorter variant is to run the try block as a script block and just redirect the errors into an unset variable ($null would be the usual one, but that's still too long):

.{ <# something that throws errors #> }2>$x

This saves five valuable bytes (if not the world).

\$\endgroup\$
5
\$\begingroup\$

You can use $s|% t*y instead [char[]]$s to split a string to char array. Given from TessellatingHeckler's answer: % t*y expands to | ForEach-Object -Method ToCharArray equiv. of "$args".ToCharArray()

For example, compare

$s|% t*y

and

[char[]]$s

and

$s.ToCharArray()

It's useful with $args especially: $args|% t*y

\$\endgroup\$
6
  • 1
    \$\begingroup\$ That's quite neat. I've used the % trick for members a few times as well, but most of my golfing predates that feature ;-). It's quite a general technique as well: Try finding a letter/wildcard combination that matches the property/method you need (and is shorter than the real thing). \$\endgroup\$
    – Joey
    Commented Jul 8, 2018 at 9:36
  • 1
    \$\begingroup\$ Another useful examples from the answer: $s|% *per for $s.toUpper() and $s|% *wer for $s.toLower(). I'm agree that's quite neat. \$\endgroup\$
    – mazzy
    Commented Jul 8, 2018 at 12:19
  • \$\begingroup\$ One more example: |% t* "ddd\:hh\:mm\:ss" for [TimeSpan].toString("ddd\:hh\:mm\:ss") \$\endgroup\$
    – mazzy
    Commented Jul 8, 2018 at 12:42
  • \$\begingroup\$ Well, that's just a waste of quotes, then; you don't need them here :-) \$\endgroup\$
    – Joey
    Commented Jul 8, 2018 at 17:54
  • 1
    \$\begingroup\$ In addition to that submission I wrote an entire outline of this feature right here in this post :) \$\endgroup\$
    – briantist
    Commented Dec 27, 2018 at 21:01
5
\$\begingroup\$

You can access static methods via instance:

$x=4,2,3
$x::Sort($x) # [Array]::Sort($x)
"$x" # 2 3 4
$x::Reverse($x) # [Array]::Reverse($x)
"$x" # 4 3 2

BigInteger suffix (n) introduced in PowerShell 7:

(0n)::Pow(4,5) # [bigint]::Pow(4,5)
(0n)::Abs(-3) # [bigint]::Abs(-3)
2*0n::Abs(-3) # some cases don't require parentheses
\$\endgroup\$
6
  • \$\begingroup\$ Thanks. n suffix introduced in PS7. So you should notice about it. $x::Reverse($x) does not work for me \$\endgroup\$
    – mazzy
    Commented Feb 16, 2021 at 6:16
  • \$\begingroup\$ @mazzy It works on TIO \$\endgroup\$
    – btnlq
    Commented Feb 16, 2021 at 7:36
  • \$\begingroup\$ TIO is highly customized enironment. moreover, powershell for windows is different from powershell for linux (see sort alias). Please check your advises with local environment. \$\endgroup\$
    – mazzy
    Commented Feb 16, 2021 at 7:53
  • 1
    \$\begingroup\$ It works in all environments that i have: 7.1.1 Core (Windows), 5.1 (Windows), code.golf (7.1.2 Core (Linux)). \$\endgroup\$
    – btnlq
    Commented Feb 16, 2021 at 8:27
  • \$\begingroup\$ Oh, that's convenient and news to me. Nice. \$\endgroup\$
    – Joey
    Commented Feb 17, 2021 at 8:09
4
\$\begingroup\$

Invoke-Expression and Get-Random can also get pipeline input instead of arguments.

For iex this allows to save parentheses on some expressions:

iex 1..5-join'+'   # won't work
iex(1..5-join'+')  # does work, but has extra parentheses
1..5-join'+'|iex   # doesn't need the parentheses

In case of random this allows a common case to be optimized a bit:

random -mi 10 31   # gets a random integer between 10 and 30
10..30|random      # much better :-)
(random 21)+10     # if needed in another expression that doesn't require extra
                   # parentheses

The latter way of using it simply selects an item from a list. The -c argument can be given to allow more than a single selection.

\$\endgroup\$
4
\$\begingroup\$

Consider storing repeated script blocks in variables, instead of using functions.

I was going to use this to save some characters in my Rock, Paper, Scissors implementation before I realized that re-writing the function as a variable made even the variable unnecessary. This could still be useful for other scripts though, where you're actually running the same code multiple times.

function Hi{echo 'Hello, World!'};Hi

vs.

$Hi={echo 'Hello, World!'};&$Hi
\$\endgroup\$
2
  • 3
    \$\begingroup\$ Useful for functions that do not take arguments. Otherwise the params(...) would take up more space than the function definition saves. Related: Use filter over function when you can do so. \$\endgroup\$
    – Joey
    Commented Nov 26, 2013 at 21:57
  • 2
    \$\begingroup\$ You could use $args to avoid the need for params; i.e. $x={$args[0]+2} (or even $x={2+"$args"}); may save a character or 2 in some circumstances. You can also combine this with another trick for multiple params: $x={$a,$b=$args;$a+$b+3} \$\endgroup\$
    – JohnLBevan
    Commented Jan 1, 2017 at 20:35
4
\$\begingroup\$

Use Boolean logic in place of if-counters in a loop

Suppose you're adding all even numbers from 1 to 10 ... 2+4+6+8+10=30

1..10|%{if($_%2-eq0){$o+=$_}};$o

You could use Boolean negation to shorten it to

1..10|%{if(!($_%2)){$o+=$_}};$o

to save a byte, but how about instead use implicit casting of Booleans to ints, and roll the conditional into the accumulator

1..10|%{$o+=$_*!($_%2)};$o

Saves 6 bytes in this example.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Jesus christ. I'm planning to do this in my production code at work, my colleagues will love me for it. \$\endgroup\$
    – Chavez
    Commented Nov 4, 2016 at 9:13
4
\$\begingroup\$

Converting floating-point numbers to integers in PowerShell is a bit of a minefield. By default the conversion does Bankers Rounding which doesn't always trim off the decimal and leave the smaller whole number, or always round .5 up to the next number like people do casually, it rounds evens one way and odds another way - this can be surprising, e.g.

PS C:\> [int]1.5
2

PS C:\> [int]2.5
2

and break codegolf calculations. Many other common languages do truncation-rounding, therefore golf questions often require truncation. You might reach for [Math]::Floor() as the next best thing, but beware this only behaves the same as truncation for positive numbers, but it takes negative numbers lower - further away from zero. [Math]::Truncate() is what you need to bring PS behaviour in line with other language's default rounding, but it's a lot of characters.

Regex replacing digits after the decimal point can help save a couple of characters:

[Math]::Truncate(1.5)
[Math]::Floor(1.5)
1.5-replace'\..*'

[Math]::Truncate($a)
[Math]::Floor($a)
$a-replace'\..*'
$a.split('.')[0]        # literal character split, not regex pattern split
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.