Listに入ってる数字の平均を計算しようとしたとき、LinqでAvarageっていうのがあったなと思ったのがきっかけで、PowerShellからLinqの拡張メソッドを呼び出す方法を調べたのでメモ。

LinqのAvarageは、引数にIEnumerableな数値リストを指定すれば、ただのSystem.Linq.Enumerableの静的メソッドなので、PowerShellでも数値の配列を渡せば使える。ただし、Object配列は渡せないので、型指定して渡す必要があることに注意が必要。

[System.Linq.Enumerable]::Average(@(30, 20, 10))
"Average" のオーバーロードで、引数の数が "1" であるものが見つかりません。
発生場所 行:1 文字:1
+ [System.Linq.Enumerable]::Average(@(30, 20, 10))
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest
[System.Linq.Enumerable]::Average([decimal[]]@(30, 20, 10))
20

平均(Average)の他にも、最大(Max)、最小(Min)、合計(Sum)も同じように使用できる。

[System.Linq.Enumerable]::Max([decimal[]]@(30, 20, 10))
30
[System.Linq.Enumerable]::Min([decimal[]]@(30, 20, 10))
10
[System.Linq.Enumerable]::Sum([decimal[]]@(30, 20, 10))
60

ちなみに、平均、最大、最小、合計の計算はMeasure-Objectコマンドレットでもできるので、こちらを使ってもよい。

引数にラムダ式が必要な拡張メソッド以外なら大体同じように実行できるので、Enumerable クラスのメソッドをざっと見て使えそうなものを覚えておくとよさそう。

以下はわりと使えそうなものの実行例。

[System.Linq.Enumerable]::Skip([string[]]@("A", "B", "C", "D", "E"), 3)
D
E
[System.Linq.Enumerable]::Reverse([string[]]@("A", "B", "C"))
C
B
A
[System.Linq.Enumerable]::LastOrDefault([string[]]@("A", "B", "C", "D", "E"))
E

1つ注意が必要なのが、上のSkipなどのようなリストを処理してリストを戻す系のもの。

戻り値がリストだった場合は、続けてLinqで処理できるようにイテレーター(反復子)になっているので、戻り値のリストから単純にインデックスで値を取り出すことができない。

[System.Linq.Enumerable]::Skip([string[]]@("A", "B", "C", "D", "E"), 3).GetType().FullName
System.Linq.Enumerable+<SkipIterator>d__31`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
[System.Linq.Enumerable]::Skip([string[]]@("A", "B", "C", "D", "E"), 3)[0]
D
E

そのため、戻り値のリストはForEach-Object(またはforeach)で列挙しながら直接使うか、列挙して配列に変換してから使うか、または、別のLinqの拡張メソッドで処理したりする必要がある。

[System.Linq.Enumerable]::Skip([string[]]@("A", "B", "C", "D", "E"), 3) | ForEach-Object { "「$_」" }
「D」
「E」
[System.Linq.Enumerable]::Skip([string[]]@("A", "B", "C", "D", "E"), 3).ForEach([string])[0]
D
$skipQuery = [System.Linq.Enumerable]::Skip([string[]]@("A", "B", "C", "D", "E"), 3)
[System.Linq.Enumerable]::ElementAt($skipQuery, 0)
D

参考:
Enumerable クラス (System.Linq) | Microsoft Learn
C# でのコレクションの反復処理 - C# | Microsoft Learn
配列について - PowerShell | Microsoft Learn
Measure-Object (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Learn