はじめに
PowerShellでLINQって使えるの?答えは「YES」です。PowerShellは.NETの上で動いているので、LINQを直接呼び出せます。今回は、身近なデータ処理タスクをLINQで解決して、その威力を体感してみましょう。
「PowerShellでもLINQっぽく書けたらな…」と思ったことはありませんか?
本記事は、そんな発想の転換からはじまった実験的なアプローチの紹介です。
C#のコードをそのまま当てはめても思った通りに動かないこともありますが、考え方を取り入れることで新しい書き方のヒントになるかもしれません。
筆者は、Windows 11 上の PowerShell 5.1 にて動作確認を行っています。
準備:3行でLINQを使えるようにする
using namespace System.Linq
using namespace System.Collections.Generic
Add-Type -AssemblyName System.Core
実践:CSVデータの高速分析
まずは、誰でも試せるようにサンプルデータを作成しましょう。
# サンプルの売上データを作成
$csvData = @"
Date,Product,Category,Amount,SalesPerson
2024-01-15,Laptop,Electronics,150000,田中
2024-01-16,Mouse,Electronics,3000,佐藤
2024-01-17,Keyboard,Electronics,8000,田中
2024-01-18,Desk,Furniture,45000,鈴木
2024-01-19,Chair,Furniture,25000,田中
2024-01-20,Monitor,Electronics,80000,佐藤
2024-01-21,Lamp,Furniture,12000,鈴木
2024-01-22,Laptop,Electronics,150000,田中
2024-01-23,Mouse,Electronics,3000,佐藤
2024-01-24,Bookshelf,Furniture,35000,鈴木
"@
# 一時ファイルに保存
$csvData | Out-File -FilePath "temp_sales.csv" -Encoding UTF8
# CSV読み込み
$salesData = Import-Csv "temp_sales.csv"
1. 通常のPowerShellでの処理
# 通常のPowerShellでカテゴリ別売上集計
Write-Host "=== 通常のPowerShell ===" -ForegroundColor Yellow
$traditionalResult = $salesData | Group-Object Category | ForEach-Object {
[PSCustomObject]@{
Category = $_.Name
TotalSales = ($_.Group | Measure-Object Amount -Sum).Sum
Count = $_.Count
}
}
$traditionalResult | Format-Table -AutoSize
2. LINQでの処理
# LINQで同じ処理
Write-Host "=== LINQ版 ===" -ForegroundColor Yellow
# カテゴリ別にグループ化
$categoryGroups = [System.Linq.Enumerable]::GroupBy($salesData, [Func[object,string]]{
param($row) return $row.Category
})
$linqResult = @()
foreach ($group in $categoryGroups) {
$totalSales = [System.Linq.Enumerable]::Sum($group, [Func[object,int]]{
param($row) return [int]$row.Amount
})
$linqResult += [PSCustomObject]@{
Category = $group.Key
TotalSales = $totalSales
Count = [System.Linq.Enumerable]::Count($group)
}
}
$linqResult | Format-Table -AutoSize
複雑なデータ変換をワンライナーで
LINQの真価は、複雑な処理を関数チェーンで表現できることです。
# 売上TOP営業担当者を、カテゴリ別に抽出
Write-Host "=== カテゴリ別TOP営業担当者 ===" -ForegroundColor Cyan
# まずカテゴリごとにグループ化
$categoryGroups = [System.Linq.Enumerable]::GroupBy($salesData, [Func[object,string]]{
param($row) return $row.Category
})
$topSalesPersons = @()
foreach ($categoryGroup in $categoryGroups) {
# カテゴリ内で営業担当者別にグループ化
$personGroups = [System.Linq.Enumerable]::GroupBy($categoryGroup, [Func[object,string]]{
param($row) return $row.SalesPerson
})
# 各営業担当者の売上合計を計算してTOP2を取得
$topPersons = [System.Linq.Enumerable]::Take(
[System.Linq.Enumerable]::OrderByDescending($personGroups, [Func[object,int]]{
param($personGroup)
return [System.Linq.Enumerable]::Sum($personGroup, [Func[object,int]]{
param($row) return [int]$row.Amount
})
}), 2
)
# 結果を作成
foreach ($personGroup in $topPersons) {
$topSalesPersons += [PSCustomObject]@{
Category = $categoryGroup.Key
SalesPerson = $personGroup.Key
TotalSales = [System.Linq.Enumerable]::Sum($personGroup, [Func[object,int]]{
param($row) return [int]$row.Amount
})
ItemCount = [System.Linq.Enumerable]::Count($personGroup)
}
}
}
$topSalesPersons | Sort-Object Category, TotalSales -Descending | Format-Table -AutoSize
文字列処理の威力を体感
# 文字列データの分析例
$textData = @(
"PowerShell は Microsoft が開発したコマンドラインシェルです",
"LINQ は Language Integrated Query の略称です",
"C# や VB.NET などの .NET 言語で使用できます",
"PowerShell でも LINQ を使用することができます"
)
Write-Host "=== 文字列分析 ===" -ForegroundColor Magenta
# 各文の単語数と文字数を分析
$textAnalysis = @()
foreach ($text in $textData) {
$words = $text -split '\s+'
$chars = $text.ToCharArray()
# 母音の数を計算
$vowelCount = [System.Linq.Enumerable]::Count(
[System.Linq.Enumerable]::Where($chars, [Func[char,bool]]{
param($c) return $c -in @('a','e','i','o','u','A','E','I','O','U','あ','い','う','え','お')
})
)
# 子音の数を計算
$consonantCount = [System.Linq.Enumerable]::Count(
[System.Linq.Enumerable]::Where($chars, [Func[char,bool]]{
param($c) return [char]::IsLetter($c) -and $c -notin @('a','e','i','o','u','A','E','I','O','U','あ','い','う','え','お')
})
)
$textAnalysis += [PSCustomObject]@{
Text = if ($text.Length -gt 30) { $text.Substring(0, 30) + "..." } else { $text }
WordCount = $words.Length
CharCount = $text.Length
VowelCount = $vowelCount
ConsonantCount = $consonantCount
}
}
$textAnalysis | Format-Table -AutoSize
まとめ:LINQがもたらす新しい視点
PowerShellでLINQを使うことで、単なる文法を超えたプログラミング的な思考が身につきます。
LINQを通じて、データを「変換の連鎖」として捉える関数型思考や、「何をしたいか」に焦点を当てる宣言的プログラミングの考え方を学べます。これにより、コードはより直感的で読みやすくなります。
また、LINQの利用を通じて、PowerShellが.NETの一部として動作していることを体感でき、.NETエコシステムの強みを活かすことができます。遅延評価の仕組みにより、大量データの処理を効率化できる点も実用的です。
通常のコマンドレットで十分な処理もありますが、複雑な変換や集計、複数コレクションの操作ではLINQの方が明快で保守しやすいケースが多くあります。
また、C#での開発経験があるエンジニアにとっては、既存のLINQロジックをPowerShellスクリプトに活用できる点も大きなメリットです。C#で培ったLINQの知識や、過去に作成したデータ処理ロジックを自動化スクリプトに応用できれば、開発効率の向上が期待できます。
もちろん、すべてがそのまま移植できるわけではありませんが、うまくハマれば実用的なケースもあるでしょう。
PowerShellユーザーの皆さん、もしも、興味ありましたら、ぜひ一度LINQの世界を体験してみてください!
参考情報