PowerShell Interview Questions with Answers
Most Asked PowerShell Interview Questions for DevOps and Automation Roles
Introduction
PowerShell is a cross‑platform automation and configuration management framework, combining a powerful command‑line shell with a full‑featured scripting language. This page compiles the most frequently asked PowerShell interview questions – from basic cmdlets and variables to advanced object‑oriented programming, modules, and automation – essential for DevOps engineers, system administrators, and cloud professionals.
Why PowerShell?
- Cross‑platform – Windows, Linux, macOS
- Object‑oriented – works with .NET objects, not text
- Powerful scripting language with full programming features
- Integrates with Azure, AWS, and other cloud platforms
- Essential for Windows administration and DevOps
- Rich ecosystem with PowerShell Gallery
Most Asked PowerShell Interview Questions
PowerShell is a cross-platform task automation and configuration management framework from Microsoft, consisting of a command-line shell and scripting language.
- Object-oriented: Works with .NET objects
- Scripting language: Full-featured programming language
- Cross-platform: Windows, Linux, macOS
- Cmdlets: Verb-Noun naming convention
- Pipeline: Pass objects between commands
# Hello World in PowerShell
Write-Host "Hello, World!"Variables in PowerShell are declared using the $ symbol. They are dynamically typed and can hold any object.
- Assignment:
$x = 10 - Dollar sign:
$variable - Constants:
Set-Variable -Name PI -Value 3.14159 -Option Constant - Global scope:
$global:x = 10 - Local scope:
$local:x = 10
# Variables in PowerShell
$x = 10 # Integer
$y = 3.14 # Float
$name = "PowerShell" # String
$is_active = $true # Boolean
Write-Host $x
Write-Host $y
Write-Host $name
Write-Host $is_activePowerShell supports various data types through the .NET framework.
- Integer:
int - Float:
double - String:
string - Boolean:
bool - Array:
array - Hashtable:
hashtable - PSCustomObject:
PSCustomObject - Null:
$null
# Data Types in PowerShell
# Integer types
$a = 10 # int
$b = 127 # int
# Floating point
$d = 3.14 # double
$e = 2.5 # double
# String
$f = "Hello PowerShell"
# Boolean
$g = $true
$h = $false
# Array
$j = @(1, "hello", 3.14)
# Indexed array
$k = @(1, 2, 3, 4, 5)
# Hashtable (dictionary)
$l = @{
"name" = "PowerShell"
"version" = 7.4
}
# PSCustomObject
$person = [PSCustomObject]@{
Name = "Alice"
Age = 25
City = "NYC"
}
# Null
$m = $null
Write-Host $a.GetType()
Write-Host $d.GetType()Functions in PowerShell are defined using the function keyword with Verb-Noun naming convention.
- Function declaration:
function Get-Name { ... } - Parameters:
param($a, $b) - Advanced functions:
function Get-User { [CmdletBinding()] param(...) } - Filter functions: Use for pipeline processing
- Script blocks:
{ param($x) $x * 2 }
# Functions in PowerShell
# Function declaration
function Add-Numbers {
param($a, $b)
return $a + $b
}
# Function with default parameters
function Greet {
param($name = "Guest")
return "Hello, $name!"
}
# Advanced function with parameters
function Get-Person {
param(
[Parameter(Mandatory=$true)]
[string]$Name,
[int]$Age = 0,
[string]$City = "Unknown"
)
return [PSCustomObject]@{
Name = $Name
Age = $Age
City = $City
}
}
# Filter function (processes pipeline input)
function Get-Evens {
process {
if ($_ % 2 -eq 0) {
return $_
}
}
}
# Function with multiple outputs
function Get-Values {
return @(1, "hello", 3.14)
}
# Using functions
Add-Numbers 5 3
Greet "Alice"
Get-Person -Name "Alice" -Age 25 -City "NYC"
1..10 | Get-EvensArrays in PowerShell are collections of items that can be of any type.
- Creation:
@(1, 2, 3, 4, 5) - Access:
$arr[0] - Functions:
ForEach-Object,Where-Object,Measure-Object - Add:
$arr += 6 - Range:
1..10
# Arrays in PowerShell
$arr = @(1, 2, 3, 4, 5)
# Map - transform each element
$doubled = $arr | ForEach-Object { $_ * 2 }
Write-Host $doubled
# Filter - select elements
$evens = $arr | Where-Object { $_ % 2 -eq 0 }
Write-Host $evens
# Reduce - aggregate
$sum = ($arr | Measure-Object -Sum).Sum
Write-Host $sum
# Array comprehension
$squares = 1..10 | ForEach-Object { $_ * $_ }
Write-Host $squares
# Push and pop
$arr += 6
Write-Host $arr
$arr = $arr[0..($arr.Length-2)]
Write-Host $arr
# Array operations
$a = @(1, 2, 3)
$b = @(4, 5, 6)
$c = for ($i=0; $i -lt $a.Length; $i++) { $a[$i] + $b[$i] }
Write-Host $cHashtables in PowerShell are key-value pairs, similar to dictionaries.
- Creation:
@{ name = "Alice"; age = 25 } - Access:
$dict["name"]or$dict.name - Add/Update:
$dict["country"] = "USA" - Keys/Values:
$dict.Keys,$dict.Values - Check:
$dict.ContainsKey("name")
# Hashtables (Dictionaries) in PowerShell
# Create hashtable
$person = @{
Name = "Alice"
Age = 25
City = "NYC"
}
# Access values
Write-Host $person["Name"]
Write-Host $person.Age
# Add/update values
$person["Country"] = "USA"
$person.Age = 26
# Get with default
$city = $person["City"] ?? "Unknown"
# Keys and values
$person.Keys
$person.Values
# Iterate over hashtable
foreach ($key in $person.Keys) {
Write-Host "$key : $($person[$key])"
}
# Delete key
$person.Remove("Country")
# Check if key exists
Write-Host $person.ContainsKey("Name")
# Hashtable from arrays
$keys = 1..5
$values = 1..5 | ForEach-Object { $_ * $_ }
$squares = @{}
for ($i=0; $i -lt $keys.Count; $i++) {
$squares[$keys[$i]] = $values[$i]
}
$squaresArrays can be used as tuples to store ordered lists of values.
- Creation:
@(1, "hello", 3.14) - Access:
$tuple[0] - Unpacking:
$a, $b, $c = $tuple - Named tuples: Use
PSCustomObject - Concatenation:
$t1 + $t2
# Arrays as Tuples in PowerShell
# Create tuple-like array
$t = @(1, "hello", 3.14, $true)
# Access elements
Write-Host $t[0]
Write-Host $t[1]
# PSCustomObject as named tuple
$person = [PSCustomObject]@{
Name = "Alice"
Age = 25
City = "NYC"
}
Write-Host $person.Name
Write-Host $person.Age
# Array unpacking
$a, $b, $c = 10, 20, 30
Write-Host "$a, $b, $c"
# Function returning multiple values
function Divide {
param($a, $b)
$quotient = [math]::Floor($a / $b)
$remainder = $a % $b
return @($quotient, $remainder)
}
$quotient, $remainder = Divide 10 3
Write-Host "Quotient: $quotient, Remainder: $remainder"
# Array concatenation
$t1 = @(1, 2, 3)
$t2 = @(4, 5, 6)
$t3 = $t1 + $t2
Write-Host $t3PowerShell provides various control flow statements including conditionals and loops.
- If-else:
if ($condition) { ... } - Ternary:
$condition ? "yes" : "no" - Switch:
switch ($value) { 1 { "One" } } - For loop:
for ($i = 1; $i -le 10; $i++) - Foreach:
foreach ($item in $array) { ... }
# Control Flow in PowerShell
# If-else statement
$age = 25
if ($age -lt 18) {
Write-Host "Minor"
} elseif ($age -lt 65) {
Write-Host "Adult"
} else {
Write-Host "Senior"
}
# Ternary operator (PowerShell 7.0+)
$status = $age -ge 18 ? "Adult" : "Minor"
Write-Host $status
# Switch statement
$value = 2
switch ($value) {
0 { Write-Host "Zero" }
1 { Write-Host "One" }
2 { Write-Host "Two" }
default { Write-Host "Other" }
}
# For loop
for ($i = 1; $i -le 5; $i++) {
Write-Host $i
}
# For loop with array
$fruits = @("apple", "banana", "orange")
foreach ($fruit in $fruits) {
Write-Host $fruit
}
# While loop
$i = 1
while ($i -le 5) {
Write-Host $i
$i++
}
# Break and continue
for ($i = 1; $i -le 10; $i++) {
if ($i -eq 6) {
break
}
if ($i % 2 -eq 0) {
continue
}
Write-Host $i
}PowerShell provides various ways to generate arrays including ranges, pipelines, and loops.
- Range:
1..10 - Pipeline:
1..10 | ForEach-Object { $_ * $_ } - Filter:
1..20 | Where-Object { $_ % 2 -eq 0 } - Function generator: Custom functions
- Conditional:
1..10 | ForEach-Object { if ($_ % 2 -eq 0) { "even" } }
# Array Generation in PowerShell
# Using range and ForEach-Object
$squares = 1..10 | ForEach-Object { $_ * $_ }
Write-Host $squares
# Filter with Where-Object
$evens = 1..20 | Where-Object { $_ % 2 -eq 0 }
Write-Host $evens
# Nested loops
$matrix = @()
for ($i = 1; $i -le 3; $i++) {
for ($j = 1; $j -le 3; $j++) {
$matrix += @($i, $j)
}
}
$matrix
# Hashtable generation
$keys = 1..5
$square_dict = @{}
foreach ($key in $keys) {
$square_dict[$key] = $key * $key
}
$square_dict
# Generator function (using function with yield)
function Get-Squares {
param($n)
for ($i = 1; $i -le $n; $i++) {
$i * $i
}
}
$sum = 0
Get-Squares 100 | ForEach-Object { $sum += $_ }
Write-Host $sum
# Conditional array
$results = 1..10 | ForEach-Object {
if ($_ % 2 -eq 0) { "even" } else { "odd" }
}
Write-Host $resultsPowerShell provides extensive string manipulation capabilities.
- Concatenation:
$str1 + " " + $str2 - Interpolation:
"Hello $name" - Functions:
Length,ToUpper,ToLower - Substring:
$text.Substring(0, 5) - Split/Join:
-split,-join
# Strings in PowerShell
# String creation
$str1 = "Hello"
$str2 = 'World'
$str3 = @"
Multi-line
string
"@
# String concatenation
$greeting = $str1 + " " + $str2
Write-Host $greeting
# String interpolation
$name = "PowerShell"
$version = 7.4
Write-Host "Welcome to $name version $version"
# String functions
$text = "Hello, World!"
Write-Host $text.Length
Write-Host $text.ToUpper()
Write-Host $text.ToLower()
Write-Host $text.Replace("World", "PowerShell")
# Substring
Write-Host $text.Substring(0, 5)
# Split and join
$words = "Hello World PowerShell" -split " "
Write-Host $words
$joined = $words -join "-"
Write-Host $joined
# String comparison
Write-Host ("hello" -eq "hello")
Write-Host ("hello" -lt "world")
# String formatting
Write-Host ("Value: {0:F2}" -f 3.14159)Modules in PowerShell are containers for code that organize and encapsulate functions, cmdlets, and variables.
- Definition: Save as .psm1 file
- Import:
Import-Module ModuleName - Export:
Export-ModuleMember -Function FunctionName - Manifest: .psd1 file
- Script module: .psm1 file
# Modules in PowerShell
# Creating a module (save as MyMath.psm1)
# function Add-Numbers { param($a, $b) return $a + $b }
# function Subtract-Numbers { param($a, $b) return $a - $b }
# Export-ModuleMember -Function Add-Numbers, Subtract-Numbers
# Using a module
# Import-Module MyMath
# Defining a module with script block
$myMath = @{
PI = 3.14159
Add = { param($a, $b) $a + $b }
Subtract = { param($a, $b) $a - $b }
}
# Using the module
$myMath.Add.Invoke(5, 3)
$myMath.Subtract.Invoke(10, 4)
$myMath.PI
# Creating a PowerShell class module
class MathHelper {
static [double]$PI = 3.14159
static [int] Add([int]$a, [int]$b) { return $a + $b }
static [int] Subtract([int]$a, [int]$b) { return $a - $b }
}
[MathHelper]::Add(5, 3)
[MathHelper]::Subtract(10, 4)
[MathHelper]::PI
# Script module with functions
# function Multiply { param($a, $b) return $a * $b }
# Export-ModuleMember -Function Multiply
# Dot sourcing a script
# . .script.ps1PowerShell supports object-oriented programming with classes, properties, and methods.
- Class definition:
class MyClass { ... } - Properties:
[string]$Name - Constructor:
MyClass() { } - Methods:
[string] GetInfo() { ... } - Inheritance:
class Child : Parent
# Classes and Types in PowerShell
# Abstract class
class Animal {
[string]$Name
[int]$Age
Animal([string]$name, [int]$age) {
$this.Name = $name
$this.Age = $age
}
[string] MakeSound() {
throw "MakeSound method must be overridden"
}
}
# Concrete class
class Dog : Animal {
Dog([string]$name, [int]$age) : base($name, $age) {}
[string] MakeSound() {
return "Woof!"
}
}
# Class with properties and methods
class Person {
[string]$Name
[int]$Age
[string]$City
Person([string]$name, [int]$age, [string]$city = "Unknown") {
$this.Name = $name
$this.Age = $age
$this.City = $city
}
[string] GetInfo() {
return "$($this.Name) is $($this.Age) years old from $($this.City)"
}
}
# Usage
$dog = [Dog]::new("Rex", 3)
$person = [Person]::new("Alice", 25)
Write-Host $dog.MakeSound()
Write-Host $person.GetInfo()
# Interface-like behavior using abstract classes
class Cat : Animal {
Cat([string]$name, [int]$age) : base($name, $age) {}
[string] MakeSound() {
return "Meow!"
}
}
$cat = [Cat]::new("Whiskers", 2)
Write-Host $cat.MakeSound()PowerShell has a rich type system based on .NET with type declarations, type checking, and type conversion.
- Type declarations:
[int]$x = 10 - Type checking:
$x -is [int] - Type conversion:
[int]"42" - Strongly typed arrays:
[int[]]$numbers = @(1,2,3) - Nullable types:
[Nullable[int]]$value
# Type System in PowerShell
# Type declarations
function Get-Description {
param([int]$x)
return "Integer: $x"
}
function Get-FloatDescription {
param([double]$x)
return "Float: $x"
}
function Get-StringDescription {
param([string]$x)
return "String: $x"
}
function Get-ArrayDescription {
param([array]$x)
return "Array: $($x -join ', ')"
}
# Strongly typed array
[int[]]$numbers = @(1, 2, 3, 4, 5)
# Type checking
$value = 42
if ($value -is [int]) {
Write-Host "Value is an integer"
}
# Type conversion
$number = [int]"42"
$float = [double]"3.14"
$string = [string]42
# Nullable types
$nullableValue = $null
if ($nullableValue -eq $null) {
Write-Host "Value is null"
}
# Type constraints
function Add-Numbers {
param(
[Parameter(Mandatory=$true)]
[int]$a,
[int]$b
)
return $a + $b
}
Add-Numbers 5 3
Get-Description 42
Get-FloatDescription 3.14
Get-StringDescription "Hello"
Get-ArrayDescription @(1, 2, 3)PowerShell provides try-catch-finally blocks for error handling with support for specific exception types.
- Try-catch:
try { ... } catch { ... } - Finally:
try { ... } finally { ... } - Throw:
throw "Error message" - Specific catch:
catch [DivideByZeroException] - $Error: Automatic error variable
# Exceptions and Errors in PowerShell
# Try-catch block
try {
# Code that might error
$result = 10 / 0
Write-Host $result
} catch [DivideByZeroException] {
Write-Host "Division by zero: $($_.Exception.Message)"
}
# Specific error handling
try {
$arr = @(1, 2, 3)
Write-Host $arr[10]
} catch [System.Management.Automation.RuntimeException] {
Write-Host "Index out of bounds!"
} catch {
Write-Host "Other error: $($_.Exception.Message)"
}
# Finally block
try {
$file = [System.IO.File]::OpenText("data.txt")
Write-Host "File opened successfully"
$file.Close()
} catch {
Write-Host "Error opening file"
} finally {
Write-Host "Cleanup performed"
}
# Throwing errors
function Divide {
param($a, $b)
if ($b -eq 0) {
throw [System.DivideByZeroException]::new("Cannot divide by zero")
}
return $a / $b
}
# Using error
try {
Divide 10 0
} catch {
Write-Host "Error: $($_.Exception.Message)"
}
# Custom error records
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
"Custom error message",
"MyErrorId",
[System.Management.Automation.ErrorCategory]::InvalidOperation,
$null
)
throw $errorRecordPowerShell provides cmdlets for file operations including reading, writing, and CSV handling.
- Read:
Get-Content - Write:
Out-File - Append:
Add-Content - CSV:
Import-Csv,Export-Csv - File info:
Get-ChildItem
# File I/O in PowerShell
# Reading files
try {
$content = Get-Content "example.txt"
Write-Host $content
} catch {
Write-Host "File not found"
}
# Reading line by line
try {
Get-Content "data.txt" | ForEach-Object {
Write-Host $_
}
} catch {
Write-Host "Error reading file"
}
# Writing files
"Hello, World!" | Out-File "output.txt"
"This is line 2" | Out-File "output.txt" -Append
# Appending to files
"Appended line" | Out-File "output.txt" -Append
# Reading CSV
$data = Import-Csv "data.csv"
$data
# Writing CSV
$data = @(
[PSCustomObject]@{Name="Alice"; Age=25; City="NYC"},
[PSCustomObject]@{Name="Bob"; Age=30; City="LA"}
)
$data | Export-Csv "output.csv" -NoTypeInformation
# File operations
$files = Get-ChildItem -Path "." -Filter "*.txt"
foreach ($file in $files) {
Write-Host $file.Name
Write-Host $file.Length
}
# Using StreamReader/StreamWriter
$writer = [System.IO.StreamWriter]::new("example.txt")
$writer.WriteLine("Hello, World!")
$writer.Close()
$reader = [System.IO.StreamReader]::new("example.txt")
$content = $reader.ReadToEnd()
$reader.Close()
Write-Host $contentPowerShell uses the PowerShell Gallery for package management and modules for code organization.
- Install:
Install-Module -Name ModuleName - Import:
Import-Module ModuleName - Gallery:
Find-Module - Manifest: New-ModuleManifest
- Update:
Update-Module
# Modules and Packages in PowerShell
# Using PowerShell Gallery
# Install-Module -Name ModuleName
# Installing a module
# Install-Module -Name PSReadLine -Scope CurrentUser
# Importing a module
# Import-Module PSReadLine
# Using a module
# Get-PSReadLineOption
# Creating a module manifest
# New-ModuleManifest -Path .MyModule.psd1 -RootModule MyModule.psm1
# Using NuGet packages
# Install-PackageProvider -Name NuGet -Force
# Install-Module -Name PowerShellGet -Force
# Using a script module
# . .MyScript.ps1
# Using a binary module
# Import-Module .MyModule.dll
# Checking installed modules
# Get-Module -ListAvailable
# Updating modules
# Update-Module -Name ModuleName
# Uninstalling modules
# Uninstall-Module -Name ModuleName
# Using a private gallery
# Register-PSRepository -Name MyRepo -SourceLocation https://myrepo.com/nuget
# Example of using a module
# Install-Module -Name Pester -Force
# Invoke-PesterPowerShell can create plots using .NET Charting or by generating HTML with JavaScript libraries.
- .NET Charting:
Add-Type -AssemblyName System.Windows.Forms.DataVisualization - Chart.js: Generate HTML
- Save:
$chart.SaveImage - Custom: SVG generation
- Web-based: HTML output
# Plotting in PowerShell
# Using .NET Charting
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization
function New-Chart {
$chart = New-Object System.Windows.Forms.DataVisualization.Charting.Chart
$chart.Size = New-Object System.Drawing.Size(800, 600)
$chart.BackColor = [System.Drawing.Color]::White
$chartArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
$chart.ChartAreas.Add($chartArea)
$series = New-Object System.Windows.Forms.DataVisualization.Charting.Series
$series.ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Line
$chart.Series.Add($series)
return $chart
}
function Add-DataPoint {
param($chart, $x, $y)
$chart.Series[0].Points.AddXY($x, $y)
}
# Create plot
$chart = New-Chart
1..10 | ForEach-Object {
Add-DataPoint $chart $_ ($_ * $_)
}
# Save chart
$chart.SaveImage("chart.png", "png")
# Using Chart.js for web-based plots
$html = @"
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<canvas id="myChart"></canvas>
<script>
const ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: [1,2,3,4,5],
datasets: [{
data: [1,4,9,16,25]
}]
}
});
</script>
</body>
</html>
"@
$html | Out-File "plot.html"PowerShell provides various data structures including arrays, hashtables, and .NET collections.
- Stack:
System.Collections.ArrayList - Queue:
System.Collections.Queue - Hashtable:
@ - Set:
System.Collections.Generic.HashSet - List:
System.Collections.Generic.List
# Data Structures in PowerShell
# Stack using ArrayList
$stack = New-Object System.Collections.ArrayList
$stack.Add(1)
$stack.Add(2)
$stack.Add(3)
$stack.RemoveAt($stack.Count - 1) # Pop
# Queue using Queue
$queue = New-Object System.Collections.Queue
$queue.Enqueue(1)
$queue.Enqueue(2)
$queue.Enqueue(3)
$queue.Dequeue() # Remove first
# Hashtable as Map
$map = @{
"Alice" = 25
"Bob" = 30
"Charlie" = 35
}
# HashSet
$set = New-Object System.Collections.Generic.HashSet[int]
$set.Add(1)
$set.Add(2)
$set.Add(2) # Won't be added
# List
$list = New-Object System.Collections.Generic.List[int]
$list.Add(1)
$list.Add(2)
$list.Add(3)
# SortedSet
$sortedSet = New-Object System.Collections.Generic.SortedSet[int]
$sortedSet.Add(3)
$sortedSet.Add(1)
$sortedSet.Add(2)
# LinkedList
$linkedList = New-Object System.Collections.Generic.LinkedList[int]
$linkedList.AddLast(1)
$linkedList.AddLast(2)
$linkedList.AddFirst(0)
# Using built-in array
$array = @(1, 2, 3, 4, 5)
# Using generic List
$list = [System.Collections.Generic.List[int]]::new()
$list.Add(1)
$list.Add(2)
$list.Add(3)
Write-Host $list.CountPowerShell provides statistical functions through Measure-Object and custom implementations.
- Mean:
Measure-Object -Average - Median: Custom function
- Standard deviation: Custom calculation
- Correlation: Manual calculation
- Quantiles: Custom implementation
# Statistics in PowerShell
# Basic statistics functions
function Get-Mean {
param($data)
return ($data | Measure-Object -Average).Average
}
function Get-Median {
param($data)
$sorted = $data | Sort-Object
$count = $sorted.Count
if ($count % 2 -eq 1) {
return $sorted[($count - 1) / 2]
} else {
return ($sorted[$count / 2 - 1] + $sorted[$count / 2]) / 2
}
}
function Get-StandardDeviation {
param($data)
$mean = Get-Mean $data
$variance = ($data | ForEach-Object { ($_ - $mean) * ($_ - $mean) } | Measure-Object -Sum).Sum / $data.Count
return [math]::Sqrt($variance)
}
function Get-Variance {
param($data)
$mean = Get-Mean $data
return ($data | ForEach-Object { ($_ - $mean) * ($_ - $mean) } | Measure-Object -Sum).Sum / $data.Count
}
function Get-Correlation {
param($x, $y)
$meanX = Get-Mean $x
$meanY = Get-Mean $y
$n = $x.Count
$sumXY = 0
$sumX2 = 0
$sumY2 = 0
for ($i = 0; $i -lt $n; $i++) {
$dx = $x[$i] - $meanX
$dy = $y[$i] - $meanY
$sumXY += $dx * $dy
$sumX2 += $dx * $dx
$sumY2 += $dy * $dy
}
return $sumXY / [math]::Sqrt($sumX2 * $sumY2)
}
function Get-Quantile {
param($data, $q)
$sorted = $data | Sort-Object
$n = $sorted.Count
$pos = ($n - 1) * $q
$base = [math]::Floor($pos)
$frac = $pos - $base
if ($frac -eq 0) {
return $sorted[$base]
} else {
return $sorted[$base] + $frac * ($sorted[$base + 1] - $sorted[$base])
}
}
# Usage
$data = 1..10
Write-Host "Mean: $(Get-Mean $data)"
Write-Host "Median: $(Get-Median $data)"
Write-Host "Standard Deviation: $(Get-StandardDeviation $data)"
Write-Host "Variance: $(Get-Variance $data)"
$x = 1..100
$y = $x | ForEach-Object { 2 * $_ + (Get-Random -Minimum -10 -Maximum 10) }
Write-Host "Correlation: $(Get-Correlation $x $y)"
Write-Host "Quantile 0.25: $(Get-Quantile $data 0.25)"
Write-Host "Quantile 0.75: $(Get-Quantile $data 0.75)"PowerShell provides linear algebra operations through custom implementations using arrays.
- Matrix multiplication:
Matrix-Multiply - Transpose:
Transpose-Matrix - Determinant:
Determinant - Eigenvalues: Complex calculation
- Inverse: Using Gauss-Jordan
# Linear Algebra in PowerShell
function New-Matrix {
param($rows, $cols)
$matrix = New-Object 'double[,]' $rows, $cols
return $matrix
}
function Matrix-Multiply {
param($A, $B)
$rows = $A.GetLength(0)
$cols = $B.GetLength(1)
$inner = $B.GetLength(0)
$result = New-Matrix $rows $cols
for ($i = 0; $i -lt $rows; $i++) {
for ($j = 0; $j -lt $cols; $j++) {
$sum = 0
for ($k = 0; $k -lt $inner; $k++) {
$sum += $A[$i, $k] * $B[$k, $j]
}
$result[$i, $j] = $sum
}
}
return $result
}
function Transpose-Matrix {
param($matrix)
$rows = $matrix.GetLength(0)
$cols = $matrix.GetLength(1)
$result = New-Matrix $cols $rows
for ($i = 0; $i -lt $rows; $i++) {
for ($j = 0; $j -lt $cols; $j++) {
$result[$j, $i] = $matrix[$i, $j]
}
}
return $result
}
function Determinant {
param($matrix)
$n = $matrix.GetLength(0)
if ($n -eq 1) {
return $matrix[0, 0]
}
if ($n -eq 2) {
return $matrix[0, 0] * $matrix[1, 1] - $matrix[0, 1] * $matrix[1, 0]
}
$det = 0
for ($j = 0; $j -lt $n; $j++) {
$subMatrix = New-Matrix ($n-1) ($n-1)
for ($i = 1; $i -lt $n; $i++) {
$col = 0
for ($k = 0; $k -lt $n; $k++) {
if ($k -ne $j) {
$subMatrix[$i-1, $col] = $matrix[$i, $k]
$col++
}
}
}
$det += [math]::Pow(-1, $j) * $matrix[0, $j] * (Determinant $subMatrix)
}
return $det
}
# Usage
$A = New-Matrix 3 3
$A[0,0] = 1; $A[0,1] = 2; $A[0,2] = 3
$A[1,0] = 4; $A[1,1] = 5; $A[1,2] = 6
$A[2,0] = 7; $A[2,1] = 8; $A[2,2] = 10
$B = New-Matrix 3 1
$B[0,0] = 1; $B[1,0] = 2; $B[2,0] = 3
$product = Matrix-Multiply $A $B
$transpose = Transpose-Matrix $A
$det = Determinant $A
Write-Host "Matrix product:"
for ($i = 0; $i -lt $product.GetLength(0); $i++) {
for ($j = 0; $j -lt $product.GetLength(1); $j++) {
Write-Host $product[$i, $j]
}
}
Write-Host "Determinant: $det"PowerShell provides the Get-Date cmdlet and DateTime class for comprehensive date handling.
- Current:
Get-Date - Create:
Get-Date "2024-01-01" - Arithmetic:
$date.AddDays(10) - Difference:
$date1 - $date2 - Formatting:
$date.ToString("yyyy-MM-dd")
# Dates and Time in PowerShell
# Current date and time
$now = Get-Date
Write-Host $now
# Date creation
$date1 = Get-Date "2024-01-01"
$date2 = Get-Date "2024-01-01 12:00:00"
Write-Host $date1
Write-Host $date2
# Date arithmetic
$date1 = $date1.AddDays(10)
Write-Host $date1
$date1 = $date1.AddMonths(2)
Write-Host $date1
# Date difference
$diff = $now - $date2
Write-Host "$($diff.Days) days, $($diff.Hours) hours"
# Formatting dates
$date = Get-Date "2024-01-01"
Write-Host $date.ToString("yyyy-MM-dd HH:mm:ss")
# Date functions
Write-Host (Get-Date).Year
Write-Host (Get-Date).Month
Write-Host (Get-Date).Day
Write-Host (Get-Date).DayOfWeek
# Date range
$start = Get-Date "2024-01-01"
$end = Get-Date "2024-01-10"
for ($date = $start; $date -le $end; $date = $date.AddDays(1)) {
Write-Host $date.ToString("yyyy-MM-dd")
}
# Timezone handling
$timezone = [System.TimeZoneInfo]::FindSystemTimeZoneById("Eastern Standard Time")
$date = [System.TimeZoneInfo]::ConvertTimeFromUtc((Get-Date).ToUniversalTime(), $timezone)
Write-Host $date
# Timestamps
$timestamp = [DateTimeOffset]::Now.ToUnixTimeSeconds()
Write-Host $timestamp
$date = [DateTimeOffset]::FromUnixTimeSeconds($timestamp).LocalDateTime
Write-Host $datePowerShell provides regex support through the -match operator, regex class, and various cmdlets.
- Match:
$text -match "hello" - Regex class:
[regex]::Match($text, "hello") - Capture groups:
$matches[1] - Replace:
$text -replace "\d+", "NUM" - Split:
[regex]::Split($text, "[\s,]+")
# Regular Expressions in PowerShell
# Match
$text = "hello world"
$match = [regex]::Match($text, "hello")
if ($match.Success) {
Write-Host "Match found"
}
# Find all
$text2 = "hello world hello again"
$matches = [regex]::Matches($text2, "hello")
Write-Host $matches.Count
# Regex with capture groups
$text3 = "Date: 2024-01-01"
$match = [regex]::Match($text3, "(d{4})-(d{2})-(d{2})")
if ($match.Success) {
Write-Host $match.Groups[1].Value # year
Write-Host $match.Groups[2].Value # month
Write-Host $match.Groups[3].Value # day
}
# Replace with regex
$replaced = [regex]::Replace("Hello 123 World", "d+", "NUM")
Write-Host $replaced
# Case insensitive
$match = [regex]::Match("HELLO world", "hello", [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
if ($match.Success) {
Write-Host "Case insensitive match found"
}
# Split with regex
$parts = [regex]::Split("Hello World PHP", "[s,]+")
$parts
# Replace callback
$result = [regex]::Replace("1 2 3 4 5", "d+", {
param($match)
return [int]$match.Value * 2
})
Write-Host $result
# Using -match operator
$text = "hello world"
if ($text -match "hello") {
Write-Host "Match found using -match"
}
# Using -replace operator
$text = "Hello 123 World"
$text -replace "d+", "NUM"PowerShell supports parallel computing through ForEach-Object -Parallel, runspaces, and workflows.
- ForEach-Object -Parallel: PowerShell 7.0+
- Runspaces:
RunspaceFactory - Workflows:
workflow - Jobs:
Start-Job - PoshRSJob: Community module
# Parallel Computing in PowerShell
# Using ForEach-Object -Parallel (PowerShell 7.0+)
$numbers = 1..10
$results = $numbers | ForEach-Object -Parallel {
Start-Sleep -Seconds 1
return $_ * $_
} -ThrottleLimit 5
# Using runspaces
$runspacePool = [runspacefactory]::CreateRunspacePool(1, 5)
$runspacePool.Open()
$jobs = @()
$numbers = 1..10
foreach ($num in $numbers) {
$powershell = [powershell]::Create()
$powershell.RunspacePool = $runspacePool
$powershell.AddScript({
param($x)
Start-Sleep -Seconds 1
return $x * $x
}).AddArgument($num) | Out-Null
$handle = $powershell.BeginInvoke()
$jobs += @{
PowerShell = $powershell
Handle = $handle
}
}
$results = @()
foreach ($job in $jobs) {
$result = $job.PowerShell.EndInvoke($job.Handle)
$results += $result
$job.PowerShell.Dispose()
}
$runspacePool.Dispose()
# Using Start-Job
$job = Start-Job -ScriptBlock {
Start-Sleep -Seconds 2
return "Task completed"
}
$result = Receive-Job $job -Wait
Write-Host $result
# Using Workflows
workflow Process-Numbers {
param($numbers)
foreach -parallel ($num in $numbers) {
Start-Sleep -Seconds 1
return $num * $num
}
}
$results = Process-Numbers -numbers (1..10)
# Using PoshRSJob
# Install-Module -Name PoshRSJob
# $jobs = 1..10 | Start-RSJob -ScriptBlock { Start-Sleep 1; $_ * $_ }
# $results = $jobs | Wait-RSJob | Receive-RSJobPowerShell supports metaprogramming through Invoke-Expression, dynamic code, and proxy functions.
- Invoke-Expression:
Invoke-Expression '$x + $y' - Script blocks: Dynamic code
- Dynamic properties:
Add-Member - Proxy functions:
ProxyCommand - Dynamic modules:
New-Module
# Metaprogramming in PowerShell
# Using Invoke-Expression for dynamic code execution
$code = '$x = 10; $y = 20; $x + $y'
$result = Invoke-Expression $code
Write-Host $result
# Dynamic function calls
function Add-Numbers {
param($a, $b)
return $a + $b
}
$functionName = 'Add-Numbers'
& $functionName 5 3
# Dynamic property access
$obj = [PSCustomObject]@{
Property1 = "Value1"
Property2 = "Value2"
}
$propertyName = "Property1"
$obj.$propertyName
# Dynamic method calls
$obj = [PSCustomObject]@{
Method1 = { return "Method 1 called" }
Method2 = { return "Method 2 called" }
}
$methodName = "Method1"
& $obj.$methodName
# Using Add-Member for dynamic properties
$obj = New-Object PSObject
Add-Member -InputObject $obj -MemberType NoteProperty -Name "DynamicProperty" -Value "Value"
# Using ScriptBlock
$scriptBlock = { param($x) return $x * 2 }
& $scriptBlock 5
# Using Proxy functions
$command = Get-Command "Get-Process"
$metadata = [System.Management.Automation.CommandMetadata]::new($command)
$proxy = [System.Management.Automation.ProxyCommand]::Create($metadata)
Invoke-Expression $proxy
# Dynamic module creation
New-Module -Name DynamicModule -ScriptBlock {
function Add-Numbers { param($a, $b) $a + $b }
Export-ModuleMember -Function Add-Numbers
} | Import-Module
Add-Numbers 5 3PowerShell can interface with C# through Add-Type, loading assemblies, and using .NET framework.
- Add-Type:
Add-Type -TypeDefinition $source - DLL:
Add-Type -Path "MyLibrary.dll" - COM objects:
New-Object -ComObject - .NET classes: Direct usage
- P/Invoke: Native methods
# Interoperability with C# in PowerShell
# Using C# code in PowerShell
$source = @"
using System;
public class MathHelper {
public static int Add(int a, int b) {
return a + b;
}
public static int Multiply(int a, int b) {
return a * b;
}
}
"@
Add-Type -TypeDefinition $source
[MathHelper]::Add(5, 3)
[MathHelper]::Multiply(4, 5)
# Using C# DLL
# Add-Type -Path "MyLibrary.dll"
# Using C# with parameters
$source = @"
using System;
public class Calculator {
public static double Sin(double x) {
return Math.Sin(x);
}
}
"@
Add-Type -TypeDefinition $source
[Calculator]::Sin(0.5)
# Using COM objects
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$workbook = $excel.Workbooks.Add()
$worksheet = $workbook.Worksheets.Item(1)
$worksheet.Cells.Item(1, 1) = "Hello from PowerShell"
$excel.Quit()
# Using .NET framework
[System.Net.WebClient]::new().DownloadString("https://api.github.com")
# Using C# generic types
$list = [System.Collections.Generic.List[int]]::new()
$list.Add(1)
$list.Add(2)
$list.Add(3)
# Using C# LINQ
$numbers = @(1, 2, 3, 4, 5)
$query = [System.Linq.Enumerable]::Where($numbers, [Func[int,bool]]{ param($x) $x -gt 2 })
$query
# Using P/Invoke
$code = @"
using System;
using System.Runtime.InteropServices;
public class NativeMethods {
[DllImport("user32.dll")]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, int type);
}
"@
Add-Type -TypeDefinition $code
[NativeMethods]::MessageBox([IntPtr]::Zero, "Hello from PowerShell", "Message", 0)PowerShell performance can be optimized through various techniques including type declarations, avoiding pipelines, and using .NET methods.
- Type declarations:
[int[]]$array - ArrayList: For large collections
- StringBuilder: For string concatenation
- foreach statement: Faster than ForEach-Object
- .NET methods: Direct usage
# Performance Optimization in PowerShell
# Performance tips
# 1. Use type declarations
function Sum-Array {
param([int[]]$array)
$sum = 0
foreach ($value in $array) {
$sum += $value
}
return $sum
}
# 2. Use ArrayList for large collections
$list = [System.Collections.ArrayList]::new()
for ($i = 0; $i -lt 10000; $i++) {
$list.Add($i) | Out-Null
}
# 3. Use StringBuilder for string concatenation
$sb = [System.Text.StringBuilder]::new()
for ($i = 0; $i -lt 1000; $i++) {
$sb.Append("$i ") | Out-Null
}
$result = $sb.ToString()
# 4. Avoid unnecessary pipeline operations
$numbers = 1..10000
$sum = 0
foreach ($num in $numbers) {
$sum += $num
}
# 5. Use script block filters for Where-Object
$numbers | Where-Object { $_ -gt 5000 }
# 6. Use foreach statement vs ForEach-Object
$numbers = 1..10000
$result = @()
foreach ($num in $numbers) {
$result += $num * 2
}
# 7. Use .NET methods when available
[System.Math]::Sqrt(100)
# 8. Use large memory page support
# Add-Type -TypeDefinition "using System; public class Memory { }"
# 9. Use PSReadLine for better performance
# Set-PSReadLineOption -HistorySaveStyle SaveIncrementally
# 10. Use PowerShell 7 for better performance
# $PSVersionTable
# 11. Use -NoProfile for scripts
# powershell.exe -NoProfile -File script.ps1
# 12. Use compression for network transfers
# Compress-Archive -Path .*.txt -DestinationPath archive.zipPowerShell provides networking capabilities through Invoke-WebRequest, .NET classes, and custom implementations.
- HTTP:
Invoke-WebRequest - REST:
Invoke-RestMethod - WebSocket: Community modules
- TCP:
System.Net.Sockets.TcpClient - DNS:
[System.Net.Dns]::GetHostAddresses
# Networking in PowerShell
# HTTP GET request
function Invoke-Rest {
param($url)
try {
$response = Invoke-WebRequest -Uri $url -Method Get
return $response.Content
} catch {
Write-Host "Error: $($_.Exception.Message)"
return $null
}
}
# Example
# $data = Invoke-Rest "https://api.github.com"
# HTTP POST request
function Send-Data {
param($url, $data)
try {
$json = $data | ConvertTo-Json
$response = Invoke-WebRequest -Uri $url -Method Post -Body $json -ContentType "application/json"
return $response.Content
} catch {
Write-Host "Error: $($_.Exception.Message)"
return $null
}
}
# Using Invoke-RestMethod
$result = Invoke-RestMethod -Uri "https://api.github.com" -Method Get
# WebSocket example
# Install-Module -Name WebSocket
# $ws = New-WebSocket -Uri "wss://echo.websocket.org"
# $ws.Send("Hello")
# $ws.Receive()
# TCP client
function Send-TCP {
param($hostname, $port, $message)
$client = New-Object System.Net.Sockets.TcpClient
$client.Connect($hostname, $port)
$stream = $client.GetStream()
$writer = New-Object System.IO.StreamWriter($stream)
$writer.WriteLine($message)
$writer.Flush()
$reader = New-Object System.IO.StreamReader($stream)
$response = $reader.ReadToEnd()
$client.Close()
return $response
}
# TCP server
function Start-TCPServer {
param($port = 8080)
$listener = New-Object System.Net.Sockets.TcpListener([System.Net.IPAddress]::Any, $port)
$listener.Start()
Write-Host "Server listening on port $port"
while ($true) {
$client = $listener.AcceptTcpClient()
$stream = $client.GetStream()
$reader = New-Object System.IO.StreamReader($stream)
$writer = New-Object System.IO.StreamWriter($stream)
$request = $reader.ReadLine()
$response = "HTTP/1.1 200 OK`r`nContent-Type: text/html`r`n`r`nHello from server!"
$writer.Write($response)
$writer.Flush()
$client.Close()
}
}
# DNS resolution
[System.Net.Dns]::GetHostAddresses("example.com")
# Ping
Test-Connection -ComputerName "example.com" -Count 4PowerShell provides built-in cmdlets for JSON encoding and decoding.
- Encode:
ConvertTo-Json - Decode:
ConvertFrom-Json - Pretty print:
ConvertTo-Json -Depth 10 - File:
Get-Content | ConvertFrom-Json - Custom: System.Text.Json
# Working with JSON in PowerShell
# Encode to JSON
$data = @{
name = "Alice"
age = 25
city = "NYC"
hobbies = @("reading", "coding")
}
$json_string = $data | ConvertTo-Json
Write-Host $json_string
# Pretty print
$pretty_json = $data | ConvertTo-Json -Depth 10
Write-Host $pretty_json
# Decode from JSON
$json_str = '{"name":"Bob","age":30,"city":"LA"}'
$parsed = $json_str | ConvertFrom-Json
Write-Host $parsed.name
Write-Host $parsed.age
# Working with arrays
$json_array = @(1, 2, 3, 4, 5) | ConvertTo-Json
Write-Host $json_array
$parsed_array = $json_array | ConvertFrom-Json
$parsed_array
# Nested structures
$nested = @{
user = @{
id = 1
profile = @{
name = "Alice"
email = "alice@example.com"
}
}
}
Write-Host ($nested | ConvertTo-Json -Depth 10)
# Read JSON from file
$data = Get-Content "data.json" | ConvertFrom-Json
# Write JSON to file
$data | ConvertTo-Json -Depth 10 | Out-File "data.json"
# Error handling
try {
$parsed = '{"invalid":"json"}' | ConvertFrom-Json
} catch {
Write-Host "JSON Error: $($_.Exception.Message)"
}
# Custom JSON serialization
$obj = [PSCustomObject]@{
Name = "Alice"
Age = 25
City = "NYC"
}
$obj | ConvertTo-Json
# Using JsonSerializer
Add-Type -AssemblyName System.Text.Json
$json = [System.Text.Json.JsonSerializer]::Serialize($data)
$parsed = [System.Text.Json.JsonSerializer]::Deserialize($json, [PSCustomObject])PowerShell testing is done using Pester, a testing framework for PowerShell.
- Pester:
Install-Module -Name Pester - Describe: Test suite
- It: Test case
- Should: Assertions
- Mock: Test doubles
# Testing in PowerShell
# Using Pester for testing
# Install-Module -Name Pester -Force
# Basic test
Describe "Math tests" {
It "Adds two numbers" {
$result = 2 + 2
$result | Should -Be 4
}
It "Checks floating point" {
$result = 0.1 + 0.2
$result | Should -BeApproximately 0.3 -Within 0.001
}
}
# Test with exceptions
Describe "Exception tests" {
It "Throws divide by zero" {
{ 10 / 0 } | Should -Throw
}
}
# Test with arrays
Describe "Array tests" {
It "Has correct length" {
$arr = @(1, 2, 3, 4, 5)
$arr.Count | Should -Be 5
}
It "Contains value" {
$arr = @(1, 2, 3, 4, 5)
$arr | Should -Contain 3
}
}
# Test with PSCustomObject
Describe "Object tests" {
It "Creates person object" {
$person = [PSCustomObject]@{
Name = "Alice"
Age = 25
}
$person.Name | Should -Be "Alice"
$person.Age | Should -Be 25
}
}
# Test with mocking
Describe "Mock tests" {
Mock Get-Date { return Get-Date "2024-01-01" }
It "Uses mocked date" {
(Get-Date).Year | Should -Be 2024
}
}
# Test with data providers
$testCases = @(
@{ a = 1; b = 2; expected = 3 }
@{ a = 0; b = 0; expected = 0 }
@{ a = -1; b = 1; expected = 0 }
)
Describe "Data-driven tests" {
It "Adds numbers <a> + <b> = <expected>" -TestCases $testCases {
param($a, $b, $expected)
($a + $b) | Should -Be $expected
}
}
# Running tests
# Invoke-Pester
# Code coverage
# Invoke-Pester -CodeCoverage *.ps1PowerShell provides various debugging tools including Write-Debug, breakpoints, and the debugger.
- Write-Debug:
Write-Debug "Message" - Breakpoints:
Set-PSBreakpoint - Debugger:
Wait-Debugger - $Error: Error variable
- Trace-Command: Tracing
# Debugging in PowerShell
# Using Write-Debug
$DebugPreference = "Continue"
function Debug-Function {
param($x)
Write-Debug "Entering function with x = $x"
$result = $x * 2
Write-Debug "Result = $result"
return $result
}
Debug-Function 5
# Using Write-Host for debugging
Write-Host "Debug message" -ForegroundColor Yellow
# Using Set-PSBreakpoint
Set-PSBreakpoint -Script .script.ps1 -Line 10
Set-PSBreakpoint -Variable x
Set-PSBreakpoint -Command Get-Process
# Using trace
Set-PSBreakpoint -Type Trace
# Using $Error variable
$Error[0] | Format-List -Force
# Using try-catch for error handling
try {
$result = 10 / 0
} catch {
Write-Host "Error: $($_.Exception.Message)"
Write-Host "Stack trace: $($_.ScriptStackTrace)"
}
# Using $PSDebugContext
if ($PSDebugContext) {
Write-Host "Debugging active"
}
# Using Debugger commands
# s (Step into)
# v (Step over)
# c (Continue)
# q (Quit)
# ? (Help)
# Using Wait-Debugger
# Wait-Debugger
# Continue execution with Step into
# Using Trace-Command
Trace-Command -Name ParameterBinding -Expression { Get-Process }
# Using Write-Warning for warnings
Write-Warning "This is a warning"
# Using Write-Error for errors
Write-Error "This is an error"
# Using Write-Information for informational messages
Write-Information "Informational message"Abstract classes in PowerShell define base classes with abstract methods that derived classes must implement.
- Abstract class:
class Animal { [string] MakeSound() { throw ... } } - Concrete class:
class Dog : Animal - Inheritance:
: base($name, $age) - Type checking:
$dog -is [Animal] - Interfaces: Using abstract classes
# Abstract Classes and Interfaces in PowerShell
# Abstract class (using abstract methods)
class Animal {
[string]$Name
[int]$Age
Animal([string]$name, [int]$age) {
$this.Name = $name
$this.Age = $age
}
[string] MakeSound() {
throw [System.NotImplementedException]::new("MakeSound method must be overridden")
}
}
# Concrete classes
class Dog : Animal {
Dog([string]$name, [int]$age) : base($name, $age) {}
[string] MakeSound() {
return "Woof!"
}
}
class Cat : Animal {
Cat([string]$name, [int]$age) : base($name, $age) {}
[string] MakeSound() {
return "Meow!"
}
}
class Sparrow : Animal {
[float]$Wingspan
Sparrow([string]$name, [int]$age, [float]$wingspan) : base($name, $age) {
$this.Wingspan = $wingspan
}
[string] MakeSound() {
return "Chirp!"
}
}
# Interface using abstract class
class SoundMaker {
[string] MakeSound() {
throw [System.NotImplementedException]::new("MakeSound method must be overridden")
}
}
class Lion : SoundMaker {
[string]$Name
[int]$Age
Lion([string]$name, [int]$age) {
$this.Name = $name
$this.Age = $age
}
[string] MakeSound() {
return "Roar!"
}
}
# Usage
$dog = [Dog]::new("Rex", 3)
$cat = [Cat]::new("Whiskers", 2)
$sparrow = [Sparrow]::new("Tweet", 1, 15.0)
$lion = [Lion]::new("Simba", 5)
Write-Host "$($dog.Name) says $($dog.MakeSound())"
Write-Host "$($cat.Name) says $($cat.MakeSound())"
Write-Host "$($sparrow.Name) says $($sparrow.MakeSound())"
Write-Host "$($lion.Name) says $($lion.MakeSound())"
# Type checking
Write-Host ($dog -is [Animal])
Write-Host ($dog -is [Dog])PowerShell supports .NET generic types for type-safe collections and functions.
- Generic List:
[System.Collections.Generic.List[int]] - Generic Dictionary:
[System.Collections.Generic.Dictionary[string,int]] - Generic HashSet:
[System.Collections.Generic.HashSet[int]] - Generic Queue:
[System.Collections.Generic.Queue[int]] - Generic Stack:
[System.Collections.Generic.Stack[int]]
# Generic Types in PowerShell
# Using generic List
$list = [System.Collections.Generic.List[int]]::new()
$list.Add(1)
$list.Add(2)
$list.Add(3)
# Using generic Dictionary
$dict = [System.Collections.Generic.Dictionary[string,int]]::new()
$dict.Add("Alice", 25)
$dict.Add("Bob", 30)
$dict.Add("Charlie", 35)
# Using generic HashSet
$set = [System.Collections.Generic.HashSet[int]]::new()
$set.Add(1)
$set.Add(2)
$set.Add(2) # Won't be added
# Using generic Queue
$queue = [System.Collections.Generic.Queue[int]]::new()
$queue.Enqueue(1)
$queue.Enqueue(2)
$queue.Enqueue(3)
# Using generic Stack
$stack = [System.Collections.Generic.Stack[int]]::new()
$stack.Push(1)
$stack.Push(2)
$stack.Push(3)
# Generic function using type parameters
function Create-GenericList {
param($type)
$listType = [System.Collections.Generic.List`1].MakeGenericType($type)
return [Activator]::CreateInstance($listType)
}
# Usage
$intList = Create-GenericList ([int])
$intList.Add(1)
$intList.Add(2)
$stringList = Create-GenericList ([string])
$stringList.Add("Hello")
$stringList.Add("World")
# Generic method with type parameter
function Get-Item {
param(
[Parameter(Mandatory=$true)]
[System.Collections.Generic.List[object]]$list,
[int]$index
)
return $list[$index]
}
# Using LINQ with generic types
$numbers = [System.Collections.Generic.List[int]]::new()
$numbers.AddRange(@(1, 2, 3, 4, 5))
$evenNumbers = [System.Linq.Enumerable]::Where($numbers, [Func[int,bool]]{ param($x) $x % 2 -eq 0 })
$evenNumbersPowerShell uses script blocks and hashtables for trait-like behavior and composition.
- Script blocks:
$Logger = { Write-Host "Log: $($args[0])" } - Composition: Using classes
- Mixins: Hashtable merging
- Traits:
Add-Member - Multiple inheritance: Using interfaces
# Traits and Composition in PowerShell
# Using script blocks as traits
$Logger = {
Write-Host "Log: $($args[0])"
}
function Use-Logger {
param($message)
& $Logger $message
}
# Mixing traits using hashtables
$UserTrait = @{
Name = ""
Age = 0
GetInfo = { return "$($this.Name) is $($this.Age) years old" }
}
$User = [PSCustomObject]@{
Name = "Alice"
Age = 25
}
$User = $User | Select-Object * -ExcludeProperty * | Add-Member -PassThru -NotePropertyMembers $UserTrait
Write-Host $User.GetInfo()
# Using classes for composition
class Person {
[string]$Name
[int]$Age
Person([string]$name, [int]$age) {
$this.Name = $name
$this.Age = $age
}
[string] GetInfo() {
return "$($this.Name) is $($this.Age) years old"
}
}
class Employee {
hidden [Person]$Person
[string]$JobTitle
Employee([string]$name, [int]$age, [string]$jobTitle) {
$this.Person = [Person]::new($name, $age)
$this.JobTitle = $jobTitle
}
[string] GetInfo() {
return "$($this.Person.GetInfo()) and works as $($this.JobTitle)"
}
}
# Composition with multiple traits
class UserWithTraits {
[hashtable]$Traits
UserWithTraits([hashtable]$traits) {
$this.Traits = $traits
}
[string] GetInfo() {
$info = @()
foreach ($key in $this.Traits.Keys) {
$info += "$key: $($this.Traits[$key])"
}
return $info -join ", "
}
}
# Usage
$employee = [Employee]::new("Alice", 25, "Developer")
Write-Host $employee.GetInfo()
$traits = @{
Name = "Bob"
Age = 30
City = "LA"
}
$user = [UserWithTraits]::new($traits)
Write-Host $user.GetInfo()PowerShell supports generators through functions that yield values, and coroutines through workflows and tasks.
- Generator:
function Get-Numbers { $i..$end } - Coroutine:
workflow - State:
return { $state++ } - Async:
[System.Threading.Tasks.Task]::Run - Lazy: Pipeline
# Generators and Coroutines in PowerShell
# Using PowerShell functions as generators
function Get-Fibonacci {
param($count)
$a = 0
$b = 1
for ($i = 0; $i -lt $count; $i++) {
$a, $b = $b, $a + $b
$a
}
}
# Using yield-like behavior with return
function Get-Numbers {
param($start, $end)
for ($i = $start; $i -le $end; $i++) {
$i
}
}
# Generator with state
function Get-Counter {
$state = 0
return {
$state++
return $state
}
}
$counter = Get-Counter
Write-Host (& $counter) # 1
Write-Host (& $counter) # 2
Write-Host (& $counter) # 3
# Generator with yield return using pipeline
function Get-Squares {
param($n)
1..$n | ForEach-Object { $_ * $_ }
}
# Coroutine using workflows
workflow Process-Items {
param($items)
foreach -parallel ($item in $items) {
Start-Sleep -Seconds 1
$item
}
}
# Using Lazy evaluation
$lazy = 1..100 | Where-Object { $_ -gt 50 } | ForEach-Object { $_ * $_ }
# Using script blocks as coroutines
function Coroutine {
$state = 0
while ($true) {
if ($state -eq 0) {
$state = 1
return "First call"
} elseif ($state -eq 1) {
$state = 2
return "Second call"
} else {
$state = 0
return "Third call"
}
}
}
$coroutine = Coroutine
for ($i = 0; $i -lt 6; $i++) {
Write-Host $coroutine
}
# Using Task for async/await
$task = [System.Threading.Tasks.Task]::Run({
Start-Sleep -Seconds 2
return "Task completed"
})
$result = $task.ResultPowerShell provides advanced array operations including matrix operations and element-wise transformations.
- Matrix ops:
Matrix-Multiply - Element-wise:
ForEach-Object - Transpose:
Transpose-Matrix - Norm: Frobenius norm
- Trace/Diagonal: Custom functions
# Advanced Array Operations
# Array initialization
$A = New-Object 'double[,]' 3,3
$B = New-Object 'double[,]' 3,3
$C = New-Object 'double[,]' 3,3
# Initialize with values
for ($i = 0; $i -lt 3; $i++) {
for ($j = 0; $j -lt 3; $j++) {
$A[$i, $j] = 0
$B[$i, $j] = 1
$C[$i, $j] = 5
}
}
# Identity matrix
$I = New-Object 'double[,]' 3,3
for ($i = 0; $i -lt 3; $i++) {
for ($j = 0; $j -lt 3; $j++) {
$I[$i, $j] = if ($i -eq $j) { 1 } else { 0 }
}
}
# Reshaping (flatten)
$arr = 1..9
$matrix = $arr | ForEach-Object -Begin { $row = @() } -Process {
$row += $_
if ($row.Count -eq 3) {
$row
$row = @()
}
}
# Transpose
function Transpose-Matrix {
param($matrix)
$rows = $matrix.GetLength(0)
$cols = $matrix.GetLength(1)
$result = New-Object 'double[,]' $cols, $rows
for ($i = 0; $i -lt $rows; $i++) {
for ($j = 0; $j -lt $cols; $j++) {
$result[$j, $i] = $matrix[$i, $j]
}
}
return $result
}
# Element-wise operations
$A = @(@(1,2,3), @(4,5,6), @(7,8,9))
$B = $A | ForEach-Object { $_ | ForEach-Object { $_ + 1 } }
$C = $A | ForEach-Object { $_ | ForEach-Object { $_ * 2 } }
$D = $A | ForEach-Object { $_ | ForEach-Object { $_ * $_ } }
# Matrix multiplication
function Matrix-Multiply {
param($A, $B)
$rows = $A.Count
$cols = $B[0].Count
$inner = $B.Count
$result = New-Object 'double[,]' $rows, $cols
for ($i = 0; $i -lt $rows; $i++) {
for ($j = 0; $j -lt $cols; $j++) {
$sum = 0
for ($k = 0; $k -lt $inner; $k++) {
$sum += $A[$i][$k] * $B[$k][$j]
}
$result[$i, $j] = $sum
}
}
return $result
}
# Norm (Frobenius)
function Get-Norm {
param($matrix)
$sum = 0
foreach ($row in $matrix) {
foreach ($val in $row) {
$sum += $val * $val
}
}
return [math]::Sqrt($sum)
}
# Trace
function Get-Trace {
param($matrix)
$sum = 0
for ($i = 0; $i -lt $matrix.Count; $i++) {
$sum += $matrix[$i][$i]
}
return $sum
}
# Diagonal
function Get-Diagonal {
param($matrix)
$result = @()
for ($i = 0; $i -lt $matrix.Count; $i++) {
$result += $matrix[$i][$i]
}
return $result
}
Write-Host "Norm: $(Get-Norm $A)"
Write-Host "Trace: $(Get-Trace $A)"
Write-Host "Diagonal: $(Get-Diagonal $A -join ', ')"PowerShell handles missing data using $null values with various operators and functions.
- Null coalescing:
$value ?? "default" - Null coalescing assignment:
$value ??= "default" - Null check:
$value -eq $null - Filter:
Where-Object { $_ -ne $null } - AllowNull:
[AllowNull()][string]$value
# Working with Missing Data (Null handling)
# Creating arrays with missing values
$data = @(1, 2, $null, 4, 5, $null, 7)
$data
# Check for null values
function Has-Null {
param($array)
return $array -contains $null
}
Write-Host "Has null: $(Has-Null $data)"
# Remove null values
$clean_data = $data | Where-Object { $_ -ne $null }
$clean_data
# Replace null values
$replaced = $data | ForEach-Object { $_ ?? 0 }
$replaced
# Operations with null values
$x = @(1, 2, $null, 4)
$y = @(5, 6, $null, 8)
$z = for ($i = 0; $i -lt $x.Count; $i++) {
if ($x[$i] -ne $null -and $y[$i] -ne $null) {
$x[$i] + $y[$i]
} else {
$null
}
}
$z
# Ignoring null values
$sum_complete = ($x | Where-Object { $_ -ne $null } | Measure-Object -Sum).Sum
Write-Host "Sum of complete data: $sum_complete"
# Null coalescing operator (PowerShell 7.0+)
$value = $null ?? "default"
Write-Host $value
# Null coalescing assignment (PowerShell 7.0+)
$value = $null
$value ??= "default"
Write-Host $value
# Optional and nullable types
function Process-Nullable {
param([AllowNull()][string]$value)
return $value ?? "null value"
}
# Working with null in hashtables
$hash = @{
Name = "Alice"
Age = $null
City = "NYC"
}
foreach ($key in $hash.Keys) {
if ($hash[$key] -eq $null) {
Write-Host "$key is null"
} else {
Write-Host "$key: $($hash[$key])"
}
}
# Using $null comparison
$arr = @{a = $null; b = 1}
Write-Host ($null -eq $arr.a) # true
Write-Host ($arr.ContainsKey('a')) # truePowerShell provides built-in sorting and searching capabilities through cmdlets and custom functions.
- Sort:
Sort-Object - Custom sort:
Sort-Object { $_[0] } - Search:
Where-Object - Binary search: Custom implementation
- Contains:
-contains
# Sorting and Searching
# Basic sorting
$arr = @(5, 2, 8, 1, 9, 3)
$sorted = $arr | Sort-Object
$sorted
# Sorting with custom comparator
$arr3 = @(@(5, "apple"), @(3, "banana"), @(8, "cherry"))
$sorted3 = $arr3 | Sort-Object { $_[0] }
$sorted3
# Sorting descending
$arr4 = @(5, 2, 8, 1, 9, 3)
$sorted4 = $arr4 | Sort-Object -Descending
$sorted4
# Sort by property
$people = @(
[PSCustomObject]@{Name="Alice"; Age=25}
[PSCustomObject]@{Name="Bob"; Age=30}
[PSCustomObject]@{Name="Charlie"; Age=25}
)
$sorted5 = $people | Sort-Object Age, Name
$sorted5
# Search functions
$arr6 = @(1, 3, 5, 7, 9, 11)
$greater_than_5 = $arr6 | Where-Object { $_ -gt 5 }
$greater_than_5
$first_greater_than_5 = $arr6 | Where-Object { $_ -gt 5 } | Select-Object -First 1
Write-Host "First greater: $first_greater_than_5"
$last_greater_than_5 = $arr6 | Where-Object { $_ -gt 5 } | Select-Object -Last 1
Write-Host "Last greater: $last_greater_than_5"
# Contains
$has_seven = $arr6 -contains 7
$has_four = $arr6 -contains 4
Write-Host "Has 7: $has_seven"
Write-Host "Has 4: $has_four"
# Binary search
function Binary-Search {
param($arr, $target)
$left = 0
$right = $arr.Count - 1
while ($left -le $right) {
$mid = [math]::Floor(($left + $right) / 2)
if ($arr[$mid] -eq $target) {
return $mid
} elseif ($arr[$mid] -lt $target) {
$left = $mid + 1
} else {
$right = $mid - 1
}
}
return -1
}
$arr7 = @(1, 2, 3, 4, 5, 6, 7)
$index = Binary-Search $arr7 5
Write-Host "Found at index: $index"PowerShell provides mathematical operations through arithmetic operators and .NET Math class.
- Arithmetic:
+,-,*,/,% - Trigonometric:
[math]::Sin,[math]::Cos - Random:
Get-Random - Statistics:
Measure-Object - Linear algebra: Custom functions
# Mathematical Operations
# Basic arithmetic
$x = 10
$y = 3
Write-Host "x + y = $($x + $y)"
Write-Host "x - y = $($x - $y)"
Write-Host "x * y = $($x * $y)"
Write-Host "x / y = $($x / $y)"
Write-Host "x % y = $($x % $y)"
Write-Host "x ^ y = $([math]::Pow($x, $y))"
# Mathematical functions
$pi = [math]::PI
Write-Host "sin(pi/4) = $([math]::Sin($pi / 4))"
Write-Host "cos(pi/4) = $([math]::Cos($pi / 4))"
Write-Host "tan(pi/4) = $([math]::Tan($pi / 4))"
Write-Host "exp(1) = $([math]::Exp(1))"
Write-Host "log(e) = $([math]::Log([math]::Exp(1)))"
Write-Host "log10(100) = $([math]::Log10(100))"
Write-Host "sqrt(9) = $([math]::Sqrt(9))"
# Special functions
Write-Host "abs(-5) = $([math]::Abs(-5))"
Write-Host "ceil(3.14) = $([math]::Ceiling(3.14))"
Write-Host "floor(3.14) = $([math]::Floor(3.14))"
Write-Host "round(3.14) = $([math]::Round(3.14))"
Write-Host "max(1, 3, 5) = $([math]::Max(1, [math]::Max(3, 5)))"
Write-Host "min(1, 3, 5) = $([math]::Min(1, [math]::Min(3, 5)))"
# Random numbers
Write-Host "Random int: $(Get-Random -Minimum 1 -Maximum 10)"
Write-Host "Random double: $(Get-Random -Minimum 0.0 -Maximum 1.0)"
# Statistics
$data = @(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Write-Host "sum = $(($data | Measure-Object -Sum).Sum)"
Write-Host "mean = $(($data | Measure-Object -Average).Average)"
Write-Host "min = $(($data | Measure-Object -Minimum).Minimum)"
Write-Host "max = $(($data | Measure-Object -Maximum).Maximum)"PowerShell provides various serialization methods including XML, JSON, CSV, and CLI XML.
- CLI XML:
Export-CliXml - JSON:
ConvertTo-Json - CSV:
Export-Csv - XML:
System.Xml.XmlDocument - Binary: BinaryFormatter
# Data Serialization
# Using Export-CliXml and Import-CliXml
$data = @{
Name = "Alice"
Age = 25
Hobbies = @("reading", "coding")
}
$data | Export-CliXml -Path "data.xml"
$imported = Import-CliXml -Path "data.xml"
$imported
# Using ConvertTo-Json and ConvertFrom-Json
$json = $data | ConvertTo-Json -Depth 10
$json | Out-File "data.json"
$parsed = Get-Content "data.json" | ConvertFrom-Json
$parsed
# Using Export-Csv and Import-Csv
$people = @(
[PSCustomObject]@{Name="Alice"; Age=25; City="NYC"}
[PSCustomObject]@{Name="Bob"; Age=30; City="LA"}
)
$people | Export-Csv -Path "people.csv" -NoTypeInformation
$imported = Import-Csv "people.csv"
$imported
# Using BinaryFormatter (deprecated)
$binary = [System.IO.MemoryStream]::new()
$formatter = [System.Runtime.Serialization.Formatters.Binary.BinaryFormatter]::new()
$formatter.Serialize($binary, $data)
$binary.Position = 0
$deserialized = $formatter.Deserialize($binary)
$deserialized
# Using XML
$xml = [System.Xml.XmlDocument]::new()
$root = $xml.CreateElement("root")
$xml.AppendChild($root) | Out-Null
foreach ($key in $data.Keys) {
$element = $xml.CreateElement($key)
$element.InnerText = $data[$key]
$root.AppendChild($element) | Out-Null
}
$xml.Save("data.xml")
# Using YAML (requires module)
# Install-Module -Name powershell-yaml
# $yaml = ConvertTo-Yaml $data
# $yaml | Out-File "data.yaml"
# $parsed = Get-Content "data.yaml" | ConvertFrom-Yaml
# Using Protobuf (requires module)
# Install-Module -Name PSProtobuf
# $bytes = ConvertTo-Protobuf $data
# $parsed = ConvertFrom-Protobuf $bytesPowerShell can interface with databases, Redis, Memcached, and execute shell commands.
- Database:
System.Data.SqlClient - SQLite: PSSQLite
- MySQL: MySql module
- Redis: Redis module
- Shell:
& "command"
# Interfacing with External Systems
# Database connections
$connectionString = "Server=localhost;Database=test;User Id=user;Password=pass;"
try {
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
$connection.Open()
$command = $connection.CreateCommand()
$command.CommandText = "SELECT * FROM users WHERE id = @id"
$command.Parameters.AddWithValue("@id", 1) | Out-Null
$reader = $command.ExecuteReader()
while ($reader.Read()) {
Write-Host "User: $($reader['name'])"
}
$connection.Close()
} catch {
Write-Host "Database error: $($_.Exception.Message)"
}
# SQLite
# Install-Module -Name PSSQLite
# $data = Invoke-SqliteQuery -DataSource "database.db" -Query "SELECT * FROM users"
# MySQL
# Install-Module -Name MySql
# $connectionString = "Server=localhost;Database=test;Uid=user;Pwd=pass;"
# $connection = New-Object MySql.Data.MySqlClient.MySqlConnection($connectionString)
# PostgreSQL
# Install-Module -Name Npgsql
# $connectionString = "Host=localhost;Database=test;Username=user;Password=pass"
# $connection = New-Object Npgsql.NpgsqlConnection($connectionString)
# Redis (requires module)
# Install-Module -Name Redis
# $redis = Connect-Redis -Host "localhost" -Port 6379
# Set-RedisKey -Key "key" -Value "value"
# $value = Get-RedisKey -Key "key"
# Executing shell commands
$output = & "ls" "-la"
Write-Host $output
# Using Start-Process
Start-Process -FilePath "notepad.exe" -WindowStyle Normal
# Using Invoke-Command
$session = New-PSSession -ComputerName "server"
Invoke-Command -Session $session -ScriptBlock { Get-Process }
# REST API
$response = Invoke-RestMethod -Uri "https://api.github.com" -Method Get
Write-Host $responseReverse a string using array reversal, manual iteration, or recursion.
- Array method:
[array]::Reverse($chars) - Manual:
for ($i = $s.Length - 1; $i -ge 0; $i--) - Recursive:
function Reverse-StringRecursive - Performance: O(n) time
# Reverse a string
function Reverse-String {
param([string]$s)
$chars = $s.ToCharArray()
[array]::Reverse($chars)
return -join $chars
}
function Reverse-StringManual {
param([string]$s)
$reversed = ""
for ($i = $s.Length - 1; $i -ge 0; $i--) {
$reversed += $s[$i]
}
return $reversed
}
function Reverse-StringRecursive {
param([string]$s)
if ($s.Length -le 1) {
return $s
}
return (Reverse-StringRecursive $s.Substring(1)) + $s[0]
}
$s = "hello"
Write-Host "Original: $s"
Write-Host "Reversed: $(Reverse-String $s)"
Write-Host "Reversed (manual): $(Reverse-StringManual $s)"
Write-Host "Reversed (recursive): $(Reverse-StringRecursive $s)"Check if a string is a palindrome by comparing characters from both ends.
- Method:
Is-Palindrome - Case insensitive:
ToLower - Ignore spaces:
Replace(" ", "") - Recursive:
Is-PalindromeRecursive
# Check palindrome
function Is-Palindrome {
param([string]$s)
$cleaned = $s.ToLower().Replace(" ", "")
$reversed = -join ($cleaned.ToCharArray() | ForEach-Object { $_ } | Sort-Object -Descending)
return $cleaned -eq $reversed
}
function Is-PalindromeManual {
param([string]$s)
$cleaned = $s.ToLower().Replace(" ", "")
$len = $cleaned.Length
for ($i = 0; $i -lt $len / 2; $i++) {
if ($cleaned[$i] -ne $cleaned[$len - 1 - $i]) {
return $false
}
}
return $true
}
function Is-PalindromeRecursive {
param([string]$s)
$cleaned = $s.ToLower().Replace(" ", "")
if ($cleaned.Length -le 1) {
return $true
}
if ($cleaned[0] -ne $cleaned[-1]) {
return $false
}
return Is-PalindromeRecursive $cleaned.Substring(1, $cleaned.Length - 2)
}
$strings = @("racecar", "hello", "A man a plan a canal Panama", "race a car")
foreach ($s in $strings) {
Write-Host "`"$s`" is palindrome: $(Is-Palindrome $s)"
}Find the maximum value using Measure-Object, iteration, or recursion.
- Built-in:
Measure-Object -Maximum - Manual:
foreach ($value in $arr) - Recursive:
Get-MaxRecursive - Edge cases: Empty array
# Find max in array
function Get-Max {
param($arr)
return ($arr | Measure-Object -Maximum).Maximum
}
function Get-MaxManual {
param($arr)
if ($arr.Count -eq 0) { return $null }
$max = $arr[0]
foreach ($value in $arr) {
if ($value -gt $max) {
$max = $value
}
}
return $max
}
function Get-MaxRecursive {
param($arr, $index = 0, $max = $null)
if ($index -ge $arr.Count) {
return $max
}
if ($max -eq $null -or $arr[$index] -gt $max) {
$max = $arr[$index]
}
return Get-MaxRecursive $arr ($index + 1) $max
}
$arr = @(1, 5, 3, 9, 2)
Write-Host "Array: $($arr -join ', ')"
Write-Host "Max: $(Get-Max $arr)"
Write-Host "Max (manual): $(Get-MaxManual $arr)"
Write-Host "Max (recursive): $(Get-MaxRecursive $arr)"Remove duplicates using Sort-Object -Unique or manual tracking.
- Built-in:
$arr | Sort-Object -Unique - Manual:
seen hashtable - Preserve order: Manual method
- Time: O(n)
# Remove duplicates
function Remove-Duplicates {
param($arr)
return $arr | Sort-Object -Unique
}
function Remove-DuplicatesManual {
param($arr)
$seen = @{}
$result = @()
foreach ($value in $arr) {
if (-not $seen.ContainsKey($value)) {
$seen[$value] = $true
$result += $value
}
}
return $result
}
$arr = @("apple", "banana", "apple", "orange", "banana", "grape")
Write-Host "Original: $($arr -join ', ')"
Write-Host "Without duplicates: $(Remove-Duplicates $arr -join ', ')"
Write-Host "Without duplicates (manual): $(Remove-DuplicatesManual $arr -join ', ')"Merge arrays using addition, Merge-Arrays, or sorted merge.
- Addition:
$arr1 + $arr2 - Sorted merge:
Merge-Sorted - Unique:
Sort-Object -Unique - Performance: O(n) time
# Merge arrays
function Merge-Arrays {
param($arr1, $arr2)
return $arr1 + $arr2
}
function Merge-Sorted {
param($arr1, $arr2)
$result = @()
$i = 0
$j = 0
while ($i -lt $arr1.Count -and $j -lt $arr2.Count) {
if ($arr1[$i] -le $arr2[$j]) {
$result += $arr1[$i]
$i++
} else {
$result += $arr2[$j]
$j++
}
}
while ($i -lt $arr1.Count) {
$result += $arr1[$i]
$i++
}
while ($j -lt $arr2.Count) {
$result += $arr2[$j]
$j++
}
return $result
}
function Merge-Unique {
param($arr1, $arr2)
return ($arr1 + $arr2) | Sort-Object -Unique
}
$arr1 = @(1, 2, 3)
$arr2 = @(4, 5, 6)
Write-Host "Merged: $(Merge-Arrays $arr1 $arr2 -join ', ')"
$sorted1 = @(1, 3, 5, 7)
$sorted2 = @(2, 4, 6, 8)
Write-Host "Merged sorted: $(Merge-Sorted $sorted1 $sorted2 -join ', ')"Convert string to number using type casting or TryParse.
- Cast:
[int]$s,[double]$s - Safe:
[double]::TryParse - Error handling:
try - Default: Return 0 on failure
# Convert string to number
function Convert-ToNumber {
param([string]$s)
return [double]$s
}
function Convert-ToInt {
param([string]$s)
return [int]$s
}
function Convert-ToFloat {
param([string]$s)
return [double]$s
}
function Convert-ToNumberSafe {
param([string]$s)
$result = 0
if ([double]::TryParse($s, [ref]$result)) {
return $result
}
return 0
}
$strings = @("42", "3.14", "hello", "123", "45.67")
foreach ($s in $strings) {
Write-Host "`"$s`" -> int: $(Convert-ToInt $s), float: $(Convert-ToFloat $s)"
}Iterate through hashtable using foreach or GetEnumerator.
- Keys:
foreach ($key in $dict.Keys) - GetEnumerator:
$dict.GetEnumerator() - Find:
$dict.ContainsKey($key) - Return:
$dict[$key] ?? $null
# Loop through dictionary (hashtable)
function Loop-Dictionary {
param($dict)
foreach ($key in $dict.Keys) {
Write-Host "$key => $($dict[$key])"
}
}
function Find-Key {
param($dict, $key)
if ($dict.ContainsKey($key)) {
return $dict[$key]
}
return $null
}
$data = @{
Name = "Alice"
Age = 25
City = "NYC"
}
Write-Host "Dictionary:"
Loop-Dictionary $data
Write-Host ""
$name = Find-Key $data "Name"
Write-Host "Name: $name"
$country = Find-Key $data "Country"
Write-Host "Country: $(if ($country -eq $null) { 'Not found' } else { $country })"Delay execution using Start-Sleep or Start-Job for async.
- Blocking:
Start-Sleep -Seconds $seconds - Async:
Start-Job - Callback:
Delay-WithCallback - Use case: Scheduling
# Delay function execution
function Delay-Seconds {
param($seconds, $callback)
Start-Sleep -Seconds $seconds
return & $callback
}
function Delay-Async {
param($seconds, $callback)
$job = Start-Job -ScriptBlock {
param($sec, $cb)
Start-Sleep -Seconds $sec
& $cb
} -ArgumentList $seconds, $callback
return $job
}
function Delay-WithCallback {
param($seconds, $callback, $resultCallback)
$job = Start-Job -ScriptBlock {
param($sec, $cb, $resultCb)
Start-Sleep -Seconds $sec
$result = & $cb
& $resultCb $result
} -ArgumentList $seconds, $callback, $resultCallback
return $job
}
function Delayed-Print {
param($message, $seconds)
Write-Host "Starting delay of $seconds seconds"
Delay-Seconds $seconds { Write-Host $message }
}
Write-Host "Delayed execution examples:"
Delayed-Print "After 2 seconds" 2
Write-Host "Main script continues"Make HTTP requests using Invoke-WebRequest or Invoke-RestMethod.
- GET:
Invoke-WebRequest -Uri $url - POST:
Invoke-WebRequest -Method Post - Headers:
-Headersparameter - Error handling:
try-catch
# HTTP GET request
function Get-Url {
param($url)
try {
$response = Invoke-WebRequest -Uri $url -Method Get -UserAgent "PowerShell Script"
return $response.Content
} catch {
Write-Host "Error: $($_.Exception.Message)"
return $null
}
}
function Post-Data {
param($url, $data)
try {
$json = $data | ConvertTo-Json
$response = Invoke-WebRequest -Uri $url -Method Post -Body $json -ContentType "application/json"
return $response.Content
} catch {
Write-Host "Error: $($_.Exception.Message)"
return $null
}
}
try {
$result = Get-Url "https://api.github.com"
if ($result) {
Write-Host $result.Substring(0, 500) + "..."
}
} catch {
Write-Host "Error: $($_.Exception.Message)"
}
# Example POST request
$postData = @{
Name = "Alice"
Age = 25
}
# $result = Post-Data "https://httpbin.org/post" $postData
# Write-Host $resultCreate promise-like behavior using script blocks with resolve/reject functions.
- Promise:
New-Promise - Then:
Thenmethod - Catch:
Catchmethod - All:
Promise.All
# Create a promise-like task
function New-Promise {
param($executor)
$result = $null
$state = "pending"
$callbacks = @()
function Resolve {
param($value)
if ($state -ne "pending") { return }
$state = "fulfilled"
$result = $value
foreach ($callback in $callbacks) {
& $callback $value
}
}
function Reject {
param($reason)
if ($state -ne "pending") { return }
$state = "rejected"
$result = $reason
foreach ($callback in $callbacks) {
& $callback $reason
}
}
$promise = [PSCustomObject]@{
State = $state
Result = $result
Then = {
param($onFulfilled, $onRejected)
$newPromise = New-Promise {
param($resolve, $reject)
$callback = {
param($value)
if ($state -eq "fulfilled" -and $onFulfilled) {
try {
$newResult = & $onFulfilled $value
& $resolve $newResult
} catch {
& $reject $_
}
} elseif ($state -eq "rejected" -and $onRejected) {
try {
$newResult = & $onRejected $value
& $resolve $newResult
} catch {
& $reject $_
}
} else {
if ($state -eq "fulfilled") { & $resolve $value }
else { & $reject $value }
}
}
if ($state -eq "pending") {
$callbacks += $callback
} else {
& $callback $result
}
}
return $newPromise
}
Catch = {
param($onRejected)
return $promise.Then.Invoke($null, $onRejected)
}
}
& $executor $resolve $reject
return $promise
}
# Usage
$promise1 = New-Promise {
param($resolve, $reject)
Start-Sleep -Seconds 1
& $resolve "Success!"
}
$promise1.Then.Invoke({
param($value)
Write-Host "Result: $value"
})
$promise2 = New-Promise {
param($resolve, $reject)
Start-Sleep -Seconds 2
& $reject "Failed!"
}
$promise2.Catch.Invoke({
param($reason)
Write-Host "Error: $reason"
})
# Wait for all promises to complete
Start-Sleep -Seconds 3Calculate factorial using recursion or iteration.
- Recursive:
function Get-Factorial { if ($n -le 1) { 1 } else { $n * (Get-Factorial ($n - 1)) } } - Iterative:
for ($i = 2; $i -le $n; $i++) - Edge cases: 0! = 1
- Performance: Iterative faster
# Factorial
function Get-Factorial {
param($n)
if ($n -le 1) {
return 1
}
return $n * (Get-Factorial ($n - 1))
}
function Get-FactorialIterative {
param($n)
$result = 1
for ($i = 2; $i -le $n; $i++) {
$result *= $i
}
return $result
}
$n = 5
Write-Host "Factorial of $n:"
Write-Host "Recursive: $(Get-Factorial $n)"
Write-Host "Iterative: $(Get-FactorialIterative $n)"Calculate Fibonacci numbers using recursion, iteration, or memoization.
- Recursive:
function Get-Fibonacci - Iterative:
for ($i = 2; $i -le $n; $i++) - Memoized:
$cache = @ - Complexity: O(n) iterative
# Fibonacci
function Get-Fibonacci {
param($n)
if ($n -le 1) {
return $n
}
return (Get-Fibonacci ($n - 1)) + (Get-Fibonacci ($n - 2))
}
function Get-FibonacciIterative {
param($n)
if ($n -le 1) {
return $n
}
$a = 0
$b = 1
for ($i = 2; $i -le $n; $i++) {
$c = $a + $b
$a = $b
$b = $c
}
return $b
}
function Get-FibonacciMemoized {
param($n)
$cache = @{}
function Fib {
param($num)
if ($num -le 1) { return $num }
if (-not $cache.ContainsKey($num)) {
$cache[$num] = (Fib ($num - 1)) + (Fib ($num - 2))
}
return $cache[$num]
}
return Fib $n
}
$n = 10
Write-Host "Fibonacci of $n:"
Write-Host "Recursive: $(Get-Fibonacci $n)"
Write-Host "Iterative: $(Get-FibonacciIterative $n)"
Write-Host "Memoized: $(Get-FibonacciMemoized $n)"Print numbers with FizzBuzz logic using conditional statements.
- If-else:
if ($i % 15 -eq 0) - Switch:
switchstatement - Array:
Invoke-FizzBuzzArray - Output:
Write-Host
# FizzBuzz
function Invoke-FizzBuzz {
param($n)
for ($i = 1; $i -le $n; $i++) {
if ($i % 15 -eq 0) {
Write-Host "FizzBuzz"
} elseif ($i % 3 -eq 0) {
Write-Host "Fizz"
} elseif ($i % 5 -eq 0) {
Write-Host "Buzz"
} else {
Write-Host $i
}
}
}
function Invoke-FizzBuzzArray {
param($n)
$result = @()
for ($i = 1; $i -le $n; $i++) {
if ($i % 15 -eq 0) {
$result += "FizzBuzz"
} elseif ($i % 3 -eq 0) {
$result += "Fizz"
} elseif ($i % 5 -eq 0) {
$result += "Buzz"
} else {
$result += $i.ToString()
}
}
return $result
}
Write-Host "FizzBuzz for 15:"
Invoke-FizzBuzz 15
Write-Host "FizzBuzz array:"
$result = Invoke-FizzBuzzArray 15
Write-Host ($result -join ", ")Find missing number using sum formula or XOR operation.
- Sum:
$total = $n * ($n + 1) / 2 - XOR:
-bxor - Time: O(n)
- Edge cases: Empty array
# Find missing number
function Find-Missing {
param($arr)
$n = $arr.Count + 1
$total = $n * ($n + 1) / 2
$sum = ($arr | Measure-Object -Sum).Sum
return $total - $sum
}
function Find-MissingXOR {
param($arr)
$n = $arr.Count + 1
$xor_all = 0
for ($i = 1; $i -le $n; $i++) {
$xor_all = $xor_all -bxor $i
}
$xor_arr = 0
foreach ($value in $arr) {
$xor_arr = $xor_arr -bxor $value
}
return $xor_all -bxor $xor_arr
}
$arr = @(1, 2, 4, 5, 6)
Write-Host "Missing number: $(Find-Missing $arr)"
Write-Host "Missing number (XOR): $(Find-MissingXOR $arr)"Find duplicates using Group-Object or manual tracking.
- Group-Object:
$arr | Group-Object | Where-Object { $_.Count -gt 1 } - Manual:
seenhashtable - Set:
HashSet - Time: O(n)
# Find duplicates
function Find-Duplicates {
param($arr)
$seen = @{}
$duplicates = @()
foreach ($value in $arr) {
if ($seen.ContainsKey($value)) {
if ($duplicates -notcontains $value) {
$duplicates += $value
}
} else {
$seen[$value] = $true
}
}
return $duplicates
}
function Find-DuplicatesCount {
param($arr)
$groups = $arr | Group-Object
return $groups | Where-Object { $_.Count -gt 1 } | ForEach-Object { $_.Name }
}
$arr = @(1, 2, 3, 2, 4, 3, 5, 6, 5)
Write-Host "Original: $($arr -join ', ')"
Write-Host "Duplicates: $(Find-Duplicates $arr -join ', ')"
Write-Host "Duplicates (count): $(Find-DuplicatesCount $arr -join ', ')"Sum array elements using Measure-Object -Sum or manual loop.
- Built-in:
$arr | Measure-Object -Sum - Manual:
foreach ($value in $arr) { $sum += $value } - Empty: Returns 0
- Performance: Built-in faster
# Sum of array
function Get-Sum {
param($arr)
return ($arr | Measure-Object -Sum).Sum
}
function Get-SumManual {
param($arr)
$sum = 0
foreach ($value in $arr) {
$sum += $value
}
return $sum
}
$arr = @(1, 2, 3, 4, 5)
Write-Host "Array: $($arr -join ', ')"
Write-Host "Sum: $(Get-Sum $arr)"
Write-Host "Sum (manual): $(Get-SumManual $arr)"Calculate average using Measure-Object -Average or manual division.
- Built-in:
$arr | Measure-Object -Average - Manual:
($sum) / $arr.Count - Integer:
[math]::Floor - Empty: Return 0
# Average of array
function Get-Average {
param($arr)
if ($arr.Count -eq 0) { return 0 }
return ($arr | Measure-Object -Average).Average
}
function Get-AverageFloat {
param($arr)
if ($arr.Count -eq 0) { return 0.0 }
return (($arr | Measure-Object -Sum).Sum) / $arr.Count
}
function Get-AverageInteger {
param($arr)
if ($arr.Count -eq 0) { return 0 }
return [math]::Floor((($arr | Measure-Object -Sum).Sum) / $arr.Count)
}
$int_arr = @(1, 2, 3, 4, 5)
$float_arr = @(1.0, 2.0, 3.0, 4.0, 5.0)
Write-Host "Average (int array): $(Get-Average $int_arr)"
Write-Host "Average (float array): $(Get-AverageFloat $float_arr)"
Write-Host "Average (integer): $(Get-AverageInteger $int_arr)"Sort arrays using Sort-Object or custom sort.
- Non-mutating:
$arr | Sort-Object - In-place:
$arr = $arr | Sort-Object - Custom:
Sort-Object { $_ } - Performance: Built-in optimized
# Sort array ascending
function Sort-Ascending {
param($arr)
return $arr | Sort-Object
}
function Sort-AscendingInPlace {
param([ref]$arr)
$arr.Value = $arr.Value | Sort-Object
}
$arr = @(5, 2, 8, 1, 9, 3)
Write-Host "Original: $($arr -join ', ')"
Write-Host "Sorted ascending: $(Sort-Ascending $arr -join ', ')"
Sort-AscendingInPlace ([ref]$arr)
Write-Host "Sorted in-place: $($arr -join ', ')"Sort descending using Sort-Object -Descending or custom comparator.
- Built-in:
$arr | Sort-Object -Descending - Custom:
Sort-Object { $_ } -Descending - In-place:
$arr = $arr | Sort-Object -Descending - Performance: Built-in optimized
# Sort array descending
function Sort-Descending {
param($arr)
return $arr | Sort-Object -Descending
}
function Sort-DescendingInPlace {
param([ref]$arr)
$arr.Value = $arr.Value | Sort-Object -Descending
}
$arr = @(5, 2, 8, 1, 9, 3)
Write-Host "Original: $($arr -join ', ')"
Write-Host "Sorted descending: $(Sort-Descending $arr -join ', ')"
Sort-DescendingInPlace ([ref]$arr)
Write-Host "Sorted in-place: $($arr -join ', ')"Flatten nested arrays using recursion or iterative stack approach.
- Recursive:
Flatten-Array - Iterative:
Flatten-ArrayIterative - One level:
$arr | ForEach-Object { $_ } - Depth: Handle arbitrary depth
# Flatten nested array
function Flatten-Array {
param($arr)
$result = @()
foreach ($item in $arr) {
if ($item -is [array]) {
$result += Flatten-Array $item
} else {
$result += $item
}
}
return $result
}
function Flatten-ArrayIterative {
param($arr)
$result = @()
$stack = $arr -as [array] | ForEach-Object { $_ } | Sort-Object -Descending
while ($stack.Count -gt 0) {
$item = $stack[-1]
$stack = $stack[0..($stack.Count - 2)]
if ($item -is [array]) {
$stack += $item -as [array] | ForEach-Object { $_ }
} else {
$result += $item
}
}
return $result
}
$nested = @(@(1, 2), @(3, 4, 5), @(6), @(7, 8, 9, 10))
$deeper = @(@(1, 2), @(3, @(4, 5)))
Write-Host "Nested: $($nested -join ', ')"
Write-Host "Flatten: $(Flatten-Array $nested -join ', ')"
Write-Host "Deeper: $($deeper -join ', ')"
Write-Host "Flatten deeper: $(Flatten-Array $deeper -join ', ')"Split array into chunks using manual slicing or loops.
- Manual:
Chunk-Array - Predicate:
Chunk-ArrayByPredicate - Use case: Batch processing
- Time: O(n)
# Chunk array
function Chunk-Array {
param($arr, $size)
$result = @()
$i = 0
while ($i -lt $arr.Count) {
$chunk = @()
for ($j = 0; $j -lt $size -and $i -lt $arr.Count; $j++) {
$chunk += $arr[$i]
$i++
}
$result += $chunk
}
return $result
}
function Chunk-ArrayByPredicate {
param($arr, $predicate)
$result = @()
$current = @()
foreach ($item in $arr) {
if (& $predicate $item) {
if ($current.Count -gt 0) {
$result += $current
$current = @()
}
$result += @($item)
} else {
$current += $item
}
}
if ($current.Count -gt 0) {
$result += $current
}
return $result
}
$arr = 1..10
Write-Host "Original: $($arr -join ', ')"
Write-Host "Chunk (size 3):"
$chunks = Chunk-Array $arr 3
foreach ($chunk in $chunks) {
Write-Host "[$($chunk -join ', ')]"
}Implement binary search using while loop or recursion.
- Iterative:
while ($left -le $right) - Recursive:
Binary-SearchRecursive - Time: O(log n)
- Precondition: Sorted array
# Binary search
function Binary-Search {
param($arr, $target)
$left = 0
$right = $arr.Count - 1
while ($left -le $right) {
$mid = [math]::Floor(($left + $right) / 2)
if ($arr[$mid] -eq $target) {
return $mid
} elseif ($arr[$mid] -lt $target) {
$left = $mid + 1
} else {
$right = $mid - 1
}
}
return -1
}
function Binary-SearchRecursive {
param($arr, $target, $left = 0, $right = $null)
if ($right -eq $null) {
$right = $arr.Count - 1
}
if ($left -gt $right) {
return -1
}
$mid = [math]::Floor(($left + $right) / 2)
if ($arr[$mid] -eq $target) {
return $mid
} elseif ($arr[$mid] -lt $target) {
return Binary-SearchRecursive $arr $target ($mid + 1) $right
} else {
return Binary-SearchRecursive $arr $target $left ($mid - 1)
}
}
$arr = @(1, 2, 3, 4, 5, 6, 7)
$target = 5
$index = Binary-Search $arr $target
Write-Host "Found $target at index: $index"
$target2 = 8
$index2 = Binary-Search $arr $target2
Write-Host "Found $target2 at index: $index2"Implement quick sort with partitioning and recursion.
- Recursive:
Quick-Sort - In-place:
Quick-SortInPlace - Pivot: First or last element
- Time: O(n log n) average
# Quick sort
function Quick-Sort {
param($arr)
if ($arr.Count -le 1) {
return $arr
}
$pivot = $arr[0]
$left = @()
$right = @()
for ($i = 1; $i -lt $arr.Count; $i++) {
if ($arr[$i] -lt $pivot) {
$left += $arr[$i]
} else {
$right += $arr[$i]
}
}
return (Quick-Sort $left) + $pivot + (Quick-Sort $right)
}
function Quick-SortInPlace {
param([ref]$arr, $low = 0, $high = $null)
if ($high -eq $null) {
$high = $arr.Value.Count - 1
}
if ($low -lt $high) {
$pi = Partition $arr $low $high
Quick-SortInPlace $arr $low ($pi - 1)
Quick-SortInPlace $arr ($pi + 1) $high
}
}
function Partition {
param([ref]$arr, $low, $high)
$pivot = $arr.Value[$high]
$i = $low - 1
for ($j = $low; $j -lt $high; $j++) {
if ($arr.Value[$j] -le $pivot) {
$i++
$temp = $arr.Value[$i]
$arr.Value[$i] = $arr.Value[$j]
$arr.Value[$j] = $temp
}
}
$temp = $arr.Value[$i + 1]
$arr.Value[$i + 1] = $arr.Value[$high]
$arr.Value[$high] = $temp
return $i + 1
}
$arr = @(5, 3, 8, 4, 2, 7, 1, 6)
Write-Host "Original: $($arr -join ', ')"
Write-Host "Quick sort: $(Quick-Sort $arr -join ', ')"
Quick-SortInPlace ([ref]$arr)
Write-Host "Quick sort (in-place): $($arr -join ', ')"Implement merge sort with divide and conquer approach.
- Divide: Split array
- Merge:
Mergefunction - Time: O(n log n)
- Space: O(n)
# Merge sort
function Merge-Sort {
param($arr)
if ($arr.Count -le 1) {
return $arr
}
$mid = [math]::Floor($arr.Count / 2)
$left = Merge-Sort $arr[0..($mid - 1)]
$right = Merge-Sort $arr[$mid..($arr.Count - 1)]
return Merge $left $right
}
function Merge {
param($left, $right)
$result = @()
$i = 0
$j = 0
while ($i -lt $left.Count -and $j -lt $right.Count) {
if ($left[$i] -le $right[$j]) {
$result += $left[$i]
$i++
} else {
$result += $right[$j]
$j++
}
}
while ($i -lt $left.Count) {
$result += $left[$i]
$i++
}
while ($j -lt $right.Count) {
$result += $right[$j]
$j++
}
return $result
}
$arr = @(5, 3, 8, 4, 2, 7, 1, 6)
Write-Host "Original: $($arr -join ', ')"
Write-Host "Merge sort: $(Merge-Sort $arr -join ', ')"Implement bubble sort with optimization to stop early if no swaps occur.
- Basic:
for ($i = 0; $i -lt $n - 1; $i++) - Optimized:
$swappedflag - Time: O(n²) worst case
- Use case: Small datasets
# Bubble sort
function Bubble-Sort {
param($arr)
$n = $arr.Count
for ($i = 0; $i -lt $n - 1; $i++) {
for ($j = 0; $j -lt $n - $i - 1; $j++) {
if ($arr[$j] -gt $arr[$j + 1]) {
$temp = $arr[$j]
$arr[$j] = $arr[$j + 1]
$arr[$j + 1] = $temp
}
}
}
return $arr
}
function Bubble-SortOptimized {
param($arr)
$n = $arr.Count
for ($i = 0; $i -lt $n - 1; $i++) {
$swapped = $false
for ($j = 0; $j -lt $n - $i - 1; $j++) {
if ($arr[$j] -gt $arr[$j + 1]) {
$temp = $arr[$j]
$arr[$j] = $arr[$j + 1]
$arr[$j + 1] = $temp
$swapped = $true
}
}
if (-not $swapped) {
break
}
}
return $arr
}
$arr = @(5, 3, 8, 4, 2, 7, 1, 6)
Write-Host "Original: $($arr -join ', ')"
Write-Host "Bubble sort: $(Bubble-Sort $arr -join ', ')"
Write-Host "Bubble sort optimized: $(Bubble-SortOptimized $arr -join ', ')"Find intersection using Where-Object or hashtable lookup.
- Where-Object:
$arr1 | Where-Object { $arr2 -contains $_ } - Hashtable:
containsKey - Time: O(n*m) or O(n+m) with set
- Unique: Returns unique values
# Intersection of arrays
function Get-Intersection {
param($arr1, $arr2)
return $arr1 | Where-Object { $arr2 -contains $_ }
}
function Get-IntersectionSet {
param($arr1, $arr2)
$set = @{}
foreach ($item in $arr2) {
$set[$item] = $true
}
$result = @()
foreach ($item in $arr1) {
if ($set.ContainsKey($item)) {
$result += $item
}
}
return $result
}
$arr1 = @("apple", "banana", "orange", "grape", "kiwi")
$arr2 = @("banana", "kiwi", "mango", "grape")
Write-Host "Intersection: $(Get-Intersection $arr1 $arr2 -join ', ')"
Write-Host "Intersection (set): $(Get-IntersectionSet $arr1 $arr2 -join ', ')"
$ints1 = @(1, 2, 3, 4, 5)
$ints2 = @(4, 5, 6, 7, 8)
Write-Host "Intersection (ints): $(Get-Intersection $ints1 $ints2 -join ', ')"Union arrays using Sort-Object -Unique or manual merge.
- Built-in:
($arr1 + $arr2) | Sort-Object -Unique - Manual:
containscheck - Time: O(n log n) with sort
- Preserve order: Manual method
# Union of arrays
function Get-Union {
param($arr1, $arr2)
return ($arr1 + $arr2) | Sort-Object -Unique
}
function Get-UnionManual {
param($arr1, $arr2)
$result = $arr1
foreach ($item in $arr2) {
if ($result -notcontains $item) {
$result += $item
}
}
return $result
}
$arr1 = @("apple", "banana", "orange")
$arr2 = @("orange", "grape", "kiwi")
Write-Host "Union: $(Get-Union $arr1 $arr2 -join ', ')"
Write-Host "Union (manual): $(Get-UnionManual $arr1 $arr2 -join ', ')"
$ints1 = @(1, 2, 3, 4)
$ints2 = @(4, 5, 6, 7)
Write-Host "Union (ints): $(Get-Union $ints1 $ints2 -join ', ')"Find difference using Where-Object or symmetric difference.
- Where-Object:
$arr1 | Where-Object { $arr2 -notcontains $_ } - Symmetric:
Get-SymmetricDifference - Time: O(n*m)
- Performance: Use hashtable for O(n)
# Difference of arrays
function Get-Difference {
param($arr1, $arr2)
return $arr1 | Where-Object { $arr2 -notcontains $_ }
}
function Get-SymmetricDifference {
param($arr1, $arr2)
$diff1 = Get-Difference $arr1 $arr2
$diff2 = Get-Difference $arr2 $arr1
return $diff1 + $diff2
}
$arr1 = @("apple", "banana", "orange", "grape")
$arr2 = @("banana", "kiwi", "grape")
Write-Host "Difference: $(Get-Difference $arr1 $arr2 -join ', ')"
Write-Host "Symmetric difference: $(Get-SymmetricDifference $arr1 $arr2 -join ', ')"
$ints1 = @(1, 2, 3, 4, 5)
$ints2 = @(4, 5, 6, 7, 8)
Write-Host "Difference (ints): $(Get-Difference $ints1 $ints2 -join ', ')"Group objects by property using Group-Object or manual grouping.
- Group-Object:
$people | Group-Object Age - Manual:
Group-ByProperty - Use case: Data aggregation
- Time: O(n)
# Group by property
function Group-ByProperty {
param($arr, $property)
$groups = @{}
foreach ($item in $arr) {
$key = $item.$property
if (-not $groups.ContainsKey($key)) {
$groups[$key] = @()
}
$groups[$key] += $item
}
return $groups
}
# Example data
$people = @(
[PSCustomObject]@{Name="Alice"; Age=25; City="NYC"}
[PSCustomObject]@{Name="Bob"; Age=30; City="LA"}
[PSCustomObject]@{Name="Charlie"; Age=25; City="NYC"}
[PSCustomObject]@{Name="David"; Age=35; City="Chicago"}
[PSCustomObject]@{Name="Eve"; Age=30; City="LA"}
)
Write-Host "Group by age:"
$byAge = Group-ByProperty $people "Age"
foreach ($age in $byAge.Keys) {
$names = $byAge[$age].Name -join ', '
Write-Host "Age $age: $names"
}
Write-Host "Group by city:"
$byCity = Group-ByProperty $people "City"
foreach ($city in $byCity.Keys) {
$names = $byCity[$city].Name -join ', '
Write-Host "City $city: $names"
}Create deep copies of objects using recursion to clone nested structures.
- Method:
Deep-Clone - Objects:
PSCustomObjectcloning - Arrays: Recursive copy
- Hashtables: Key-value copy
# Deep clone object
function Deep-Clone {
param($obj)
if ($obj -is [array]) {
$result = @()
foreach ($item in $obj) {
$result += Deep-Clone $item
}
return $result
} elseif ($obj -is [hashtable]) {
$result = @{}
foreach ($key in $obj.Keys) {
$result[$key] = Deep-Clone $obj[$key]
}
return $result
} elseif ($obj -is [PSCustomObject]) {
$result = [PSCustomObject]@{}
foreach ($prop in $obj.PSObject.Properties) {
$result | Add-Member -MemberType NoteProperty -Name $prop.Name -Value (Deep-Clone $prop.Value)
}
return $result
} else {
return $obj
}
}
# Example
$address = [PSCustomObject]@{Street="123 Main St"; City="NYC"}
$person = [PSCustomObject]@{Name="Alice"; Address=$address}
$cloned = Deep-Clone $person
$cloned.Address.Street = "456 Oak St"
Write-Host "Original: $($person.Address.Street)"
Write-Host "Cloned: $($cloned.Address.Street)"Perform immutable updates on nested data structures using path-based updates.
- Method:
Update-Immutable - Path: Dot notation
- Recursive: Helper function
- Use case: State management
# Immutable update
function Update-Immutable {
param($obj, $path, $value)
$parts = $path -split '.'
if ($parts.Count -eq 1) {
$result = $obj.Clone()
$result[$parts[0]] = $value
return $result
}
$first = $parts[0]
$rest = $parts[1..($parts.Count - 1)] -join '.'
$result = $obj.Clone()
if ($result.ContainsKey($first)) {
$result[$first] = Update-Immutable $result[$first] $rest $value
} else {
$result[$first] = Update-Immutable @{} $rest $value
}
return $result
}
$state = @{user = @{name = "Alice"; age = 25}}
$newState = Update-Immutable $state "user.age" 26
Write-Host "Original: $($state.user.age)"
Write-Host "Updated: $($newState.user.age)"Implement pipe function for left-to-right function composition.
- Method:
Pipe - Implementation:
foreachloop - Direction: Left to right
- Use case: Function chaining
# Pipe function
function Pipe {
param($value, [scriptblock[]]$functions)
$result = $value
foreach ($fn in $functions) {
$result = & $fn $result
}
return $result
}
function Pipe-Operator {
param($value, $functions)
$result = $value
foreach ($fn in $functions) {
$result = & $fn $result
}
return $result
}
$double = { param($x) $x * 2 }
$addTen = { param($x) $x + 10 }
$square = { param($x) $x * $x }
$result = Pipe 5 $double, $addTen, $square
Write-Host "Pipe: $result"
# Using pipeline with functions
function Double { process { $_ * 2 } }
function AddTen { process { $_ + 10 } }
function Square { process { $_ * $_ } }
$result2 = 5 | Double | AddTen | Square
Write-Host "Pipeline: $result2"Implement compose function for right-to-left function composition.
- Method:
Compose - Implementation: Reverse order
- Direction: Right to left
- Use case: Function composition
# Compose function
function Compose {
param([scriptblock[]]$functions)
return {
param($value)
$result = $value
foreach ($fn in ($functions | Sort-Object -Descending)) {
$result = & $fn $result
}
return $result
}
}
$double = { param($x) $x * 2 }
$addTen = { param($x) $x + 10 }
$square = { param($x) $x * $x }
$composed = Compose $double, $addTen, $square
$result = & $composed 5
Write-Host "Composed: $result"
# Alternative compose using filter
function Compose-Filter {
param($functions)
return {
param($value)
$result = $value
foreach ($fn in ($functions | Sort-Object -Descending)) {
$result = & $fn $result
}
return $result
}
}
$composed2 = Compose-Filter $double, $addTen, $square
$result2 = & $composed2 5
Write-Host "Composed filter: $result2"Implement memoization to cache function results based on arguments.
- Method:
Memoize - Cache:
hashtable - Limit:
Memoize-WithLimit - Multiple args:
Memoize-Multiple
# Memoization
function Memoize {
param($fn)
$cache = @{}
return {
param($arg)
if (-not $cache.ContainsKey($arg)) {
$cache[$arg] = & $fn $arg
}
return $cache[$arg]
}
}
function Memoize-Multiple {
param($fn)
$cache = @{}
return {
param($args)
$key = $args -join '|'
if (-not $cache.ContainsKey($key)) {
$cache[$key] = & $fn $args
}
return $cache[$key]
}
}
function Memoize-WithLimit {
param($fn, $limit)
$cache = @{}
$keys = @()
return {
param($arg)
if (-not $cache.ContainsKey($arg)) {
if ($keys.Count -ge $limit) {
$oldest = $keys[0]
$keys = $keys[1..($keys.Count - 1)]
$cache.Remove($oldest)
}
$cache[$arg] = & $fn $arg
$keys += $arg
}
return $cache[$arg]
}
}
# Example: Fibonacci with memoization
$fib = Memoize {
param($n)
if ($n -le 1) { return $n }
return (& $fib ($n - 1)) + (& $fib ($n - 2))
}
$start = Get-Date
Write-Host "Fibonacci(35): $( & $fib 35 )"
$time1 = (Get-Date) - $start
Write-Host "Time: $($time1.TotalSeconds)s"
$start2 = Get-Date
Write-Host "Fibonacci(35) again: $( & $fib 35 )"
$time2 = (Get-Date) - $start2
Write-Host "Time: $($time2.TotalSeconds)s"Implement once function that ensures a function is called only once.
- Method:
Once - Flag:
$called - Reset:
Once-WithReset - Result: Cached result
# Once function
function Once {
param($fn)
$called = $false
$result = $null
return {
param($args)
if (-not $called) {
$called = $true
$result = & $fn $args
}
return $result
}
}
function Once-WithReset {
param($fn)
$called = $false
$result = $null
$reset = {
$called = $false
$result = $null
}
$fnOnce = {
param($args)
if (-not $called) {
$called = $true
$result = & $fn $args
}
return $result
}
return @{
Function = $fnOnce
Reset = $reset
}
}
$initialize = Once {
param($value)
Write-Host "Initialized with $value"
return $value * 2
}
Write-Host "First call: $( & $initialize 10 )"
Write-Host "Second call: $( & $initialize 20 )"
$init = Once-WithReset {
param($value)
Write-Host "Initialized with $value"
return $value * 2
}
Write-Host "First with reset: $( & $init.Function 10 )"
& $init.Reset
Write-Host "After reset: $( & $init.Function 20 )"Implement debounce with leading edge execution using timers.
- Method:
Debounce-Leading - State:
$lastCall - Timer:
System.Timers.Timer - Use case: Rate limiting
# Debounce with leading edge
function Debounce-Leading {
param($fn, $delay)
$lastCall = 0
$timeout = $null
return {
param($args)
$now = (Get-Date).Ticks / 10000000.0
if ($now - $lastCall -ge $delay) {
$lastCall = $now
return & $fn $args
}
if ($timeout -eq $null) {
$timeout = @{
Start = $now
Args = $args
}
$timer = [System.Timers.Timer]::new(($delay - ($now - $lastCall)) * 1000)
$timer.Add_Elapsed({
$lastCall = (Get-Date).Ticks / 10000000.0
& $fn $timeout.Args
$timeout = $null
$timer.Dispose()
})
$timer.Start()
}
}
}
function Debounce-LeadingSimple {
param($fn, $delay)
$lastCall = 0
return {
param($args)
$now = (Get-Date).Ticks / 10000000.0
if ($now - $lastCall -ge $delay) {
$lastCall = $now
return & $fn $args
}
return $null
}
}
$debounced = Debounce-LeadingSimple {
param($value)
Write-Host "Processing: $value"
} 2
Write-Host "Call 1: $( & $debounced 1 )"
Write-Host "Call 2: $( & $debounced 2 )"
Start-Sleep -Seconds 3
Write-Host "Call 3: $( & $debounced 3 )"Implement throttle with leading edge execution based on time since last call.
- Method:
Throttle-Leading - State:
$lastCall - Skipped: Track skipped calls
- Trailing:
Throttle-WithTrailing
# Throttle with leading edge
function Throttle-Leading {
param($fn, $delay)
$lastCall = 0
return {
param($args)
$now = (Get-Date).Ticks / 10000000.0
if ($now - $lastCall -ge $delay) {
$lastCall = $now
return & $fn $args
}
return $null
}
}
function Throttle-LeadingWithSkipped {
param($fn, $delay)
$lastCall = 0
$skipped = 0
return {
param($args)
$now = (Get-Date).Ticks / 10000000.0
if ($now - $lastCall -ge $delay) {
if ($skipped -gt 0) {
Write-Host "Skipped $skipped calls"
$skipped = 0
}
$lastCall = $now
return & $fn $args
}
$skipped++
return $null
}
}
function Throttle-WithTrailing {
param($fn, $delay)
$lastCall = 0
$pending = $null
$timer = $null
return {
param($args)
$now = (Get-Date).Ticks / 10000000.0
if ($now - $lastCall -ge $delay) {
$lastCall = $now
return & $fn $args
}
$pending = $args
if ($timer -eq $null) {
$remaining = $delay - ($now - $lastCall)
$timer = [System.Timers.Timer]::new($remaining * 1000)
$timer.Add_Elapsed({
$lastCall = (Get-Date).Ticks / 10000000.0
if ($pending -ne $null) {
& $fn $pending
$pending = $null
}
$timer.Dispose()
$timer = $null
})
$timer.Start()
}
}
}
$throttled = Throttle-Leading {
param($value)
Write-Host "Processing: $value"
} 2
Write-Host "Call 1: $( & $throttled 1 )"
Write-Host "Call 2: $( & $throttled 2 )"
Start-Sleep -Seconds 3
Write-Host "Call 3: $( & $throttled 3 )"Implement deep equality comparison for nested structures.
- Method:
Deep-Equal - Primitive:
-eq - Arrays: Recursive compare
- Objects: Compare properties
# Deep equal
function Deep-Equal {
param($obj1, $obj2)
if ($obj1 -eq $obj2) {
return $true
}
if ($obj1.GetType() -ne $obj2.GetType()) {
return $false
}
if ($obj1 -is [array] -and $obj2 -is [array]) {
if ($obj1.Count -ne $obj2.Count) {
return $false
}
for ($i = 0; $i -lt $obj1.Count; $i++) {
if (-not (Deep-Equal $obj1[$i] $obj2[$i])) {
return $false
}
}
return $true
}
if ($obj1 -is [hashtable] -and $obj2 -is [hashtable]) {
if ($obj1.Count -ne $obj2.Count) {
return $false
}
foreach ($key in $obj1.Keys) {
if (-not $obj2.ContainsKey($key)) {
return $false
}
if (-not (Deep-Equal $obj1[$key] $obj2[$key])) {
return $false
}
}
return $true
}
if ($obj1 -is [PSCustomObject] -and $obj2 -is [PSCustomObject]) {
$props1 = $obj1.PSObject.Properties
$props2 = $obj2.PSObject.Properties
if ($props1.Count -ne $props2.Count) {
return $false
}
foreach ($prop in $props1) {
if (-not ($obj2 | Get-Member -Name $prop.Name)) {
return $false
}
if (-not (Deep-Equal $prop.Value $obj2.$($prop.Name))) {
return $false
}
}
return $true
}
return $obj1 -eq $obj2
}
$obj1 = @{a = 1; b = @{c = 2}}
$obj2 = @{a = 1; b = @{c = 2}}
$obj3 = @{a = 1; b = @{c = 3}}
Write-Host "obj1 == obj2: $(Deep-Equal $obj1 $obj2)"
Write-Host "obj1 == obj3: $(Deep-Equal $obj1 $obj3)"Implement observable pattern with subscription and notification.
- Observable:
Observableclass - Subscribe:
Subscribemethod - Notify:
Notifymethod - Stateful:
StatefulObservable
# Observable pattern
class Observable {
[hashtable]$Subscribers
Observable() {
$this.Subscribers = @{}
}
[string] Subscribe([scriptblock]$callback) {
$id = [guid]::NewGuid().ToString()
$this.Subscribers[$id] = $callback
return $id
}
[void] Unsubscribe([string]$id) {
$this.Subscribers.Remove($id)
}
[void] Notify($data) {
foreach ($callback in $this.Subscribers.Values) {
& $callback $data
}
}
[void] Clear() {
$this.Subscribers.Clear()
}
}
class StatefulObservable : Observable {
[object]$State
StatefulObservable($initialState) : base() {
$this.State = $initialState
}
[void] SetState($newState) {
$this.State = $newState
$this.Notify($newState)
}
[object] GetState() {
return $this.State
}
}
$observable = [Observable]::new()
$id1 = $observable.Subscribe({ param($data) Write-Host "Observer1: $data" })
$id2 = $observable.Subscribe({ param($data) Write-Host "Observer2: $data" })
Write-Host "Notifying observers:"
$observable.Notify("Hello, World!")
$observable.Unsubscribe($id1)
Write-Host "After unsubscribing observer1:"
$observable.Notify("Hello again!")
$stateful = [StatefulObservable]::new(0)
$stateful.Subscribe({ param($state) Write-Host "State changed to: $state" })
Write-Host "Current state: $($stateful.GetState())"
$stateful.SetState(10)
$stateful.SetState(20)Implement singleton pattern to ensure only one instance exists.
- Class:
Singletonclass - Instance:
static $Instance - Private: Constructor
- Closure:
New-Singleton
# Singleton pattern
class Singleton {
static [Singleton]$Instance = $null
[hashtable]$Data = @{}
Singleton() {}
static [Singleton] GetInstance() {
if ($null -eq [Singleton]::Instance) {
[Singleton]::Instance = [Singleton]::new()
}
return [Singleton]::Instance
}
[void] Set([string]$key, $value) {
$this.Data[$key] = $value
}
$Get([string]$key) {
return $this.Data[$key] ?? $null
}
}
# Alternative singleton using script block
function New-Singleton {
param($init)
$instance = $null
return {
if ($instance -eq $null) {
Write-Host "Initializing singleton"
$instance = & $init
}
return $instance
}
}
$getConfig = New-Singleton {
return @{Name = "App"; Version = 1.0}
}
$config1 = & $getConfig
$config2 = & $getConfig
Write-Host "config1 == config2: $($config1 -eq $config2)"
Write-Host "config1 name: $($config1.Name)"
$singleton1 = [Singleton]::GetInstance()
$singleton2 = [Singleton]::GetInstance()
Write-Host "singleton1 == singleton2: $($singleton1 -eq $singleton2)"
$singleton1.Set("key", "value")
Write-Host "singleton2 get: $($singleton2.Get("key"))"Implement factory pattern for creating objects without specifying concrete classes.
- Factory:
UserFactory - Create:
Createmethod - Specific:
CreateAdmin,CreateGuest - Type: Switch on type
# Factory pattern
class User {
[string]$Name
[string]$Type
User([string]$name, [string]$type) {
$this.Name = $name
$this.Type = $type
}
}
class Admin : User {
Admin([string]$name) : base($name, "admin") {}
}
class Guest : User {
Guest([string]$name) : base($name, "guest") {}
}
class RegularUser : User {
RegularUser([string]$name) : base($name, "regular") {}
}
class UserFactory {
static [User] Create([string]$type, [string]$name) {
switch ($type) {
"admin" { return [Admin]::new($name) }
"guest" { return [Guest]::new($name) }
default { return [RegularUser]::new($name) }
}
}
static [Admin] CreateAdmin([string]$name) {
return [Admin]::new($name)
}
static [Guest] CreateGuest([string]$name) {
return [Guest]::new($name)
}
static [RegularUser] CreateRegular([string]$name) {
return [RegularUser]::new($name)
}
}
$user1 = [UserFactory]::Create("admin", "Alice")
$user2 = [UserFactory]::Create("guest", "Bob")
$user3 = [UserFactory]::Create("regular", "Charlie")
Write-Host "$($user1.Name) is $($user1.Type)"
Write-Host "$($user2.Name) is $($user2.Type)"
Write-Host "$($user3.Name) is $($user3.Type)"Implement strategy pattern with interchangeable payment methods.
- Strategy:
PaymentStrategy - Context:
PaymentContext - Execute:
ExecutePayment - Decorator:
DiscountDecorator
# Strategy pattern
class PaymentStrategy {
[string]$Name
PaymentStrategy([string]$name) { $this.Name = $name }
virtual [void] Pay([double]$amount) {
Write-Host "Paid $amount with $($this.Name)"
}
}
class CreditCardStrategy : PaymentStrategy {
CreditCardStrategy() : base("Credit Card") {}
}
class PayPalStrategy : PaymentStrategy {
PayPalStrategy() : base("PayPal") {}
}
class CryptoStrategy : PaymentStrategy {
CryptoStrategy() : base("Crypto") {}
}
class PaymentContext {
[PaymentStrategy]$Strategy
PaymentContext([PaymentStrategy]$strategy) {
$this.Strategy = $strategy
}
[void] SetStrategy([PaymentStrategy]$strategy) {
$this.Strategy = $strategy
}
[void] ExecutePayment([double]$amount) {
$this.Strategy.Pay($amount)
}
}
# Usage
$context = [PaymentContext]::new([CreditCardStrategy]::new())
$context.ExecutePayment(100.0)
$context.SetStrategy([PayPalStrategy]::new())
$context.ExecutePayment(50.0)
$context.SetStrategy([CryptoStrategy]::new())
$context.ExecutePayment(75.0)
# With discount decorator
class DiscountDecorator : PaymentStrategy {
[PaymentStrategy]$InnerStrategy
[double]$Discount
DiscountDecorator([PaymentStrategy]$strategy, [double]$discount) : base("") {
$this.InnerStrategy = $strategy
$this.Discount = $discount
}
[void] Pay([double]$amount) {
$discounted = $amount * (1 - $this.Discount)
Write-Host "Applied discount of $($this.Discount * 100)%"
$this.InnerStrategy.Pay($discounted)
}
}
$discounted = [DiscountDecorator]::new([PayPalStrategy]::new(), 0.1)
$discounted.Pay(100.0)Implement observer pattern with subject and observer classes.
- Subject:
ConcreteSubject - Observer:
Observer - Attach:
Attachmethod - Notify:
SetStatemethod
# Observer pattern
class Observer {
[string]$Name
Observer([string]$name) {
$this.Name = $name
}
[void] Update($data) {
Write-Host "Observer $($this.Name) received: $data"
}
}
class Subject {
[hashtable]$Observers = @{}
[void] Attach([Observer]$observer) {
$id = [guid]::NewGuid().ToString()
$this.Observers[$id] = $observer
}
[void] Detach([Observer]$observer) {
$keys = $this.Observers.Keys
foreach ($key in $keys) {
if ($this.Observers[$key] -eq $observer) {
$this.Observers.Remove($key)
break
}
}
}
[void] Notify($data) {
foreach ($observer in $this.Observers.Values) {
$observer.Update($data)
}
}
}
class ConcreteSubject : Subject {
$State
[void] SetState($state) {
$this.State = $state
$this.Notify($state)
}
$GetState() {
return $this.State
}
}
class DerivedObserver : Observer {
[scriptblock]$Transform
DerivedObserver([string]$name, [scriptblock]$transform) : base($name) {
$this.Transform = $transform
}
[void] Update($data) {
$transformed = & $this.Transform $data
Write-Host "Derived observer $($this.Name): $transformed"
}
}
# Usage
$subject = [ConcreteSubject]::new()
$observer1 = [Observer]::new("1")
$observer2 = [Observer]::new("2")
$observer3 = [DerivedObserver]::new("3", { param($data) $data.ToUpper() })
$subject.Attach($observer1)
$subject.Attach($observer2)
$subject.Attach($observer3)
Write-Host "Setting state:"
$subject.SetState("Hello, World!")
$subject.SetState("Another update")
$subject.Detach($observer1)
Write-Host "After detaching observer1:"
$subject.SetState("Final state")Implement decorator pattern for adding features to coffee.
- Component:
BasicCoffee - Decorator:
CoffeeDecorator - Additions:
MilkDecorator,SugarDecorator - Chaining: Nested decorators
# Decorator pattern
class Coffee {
virtual [double] GetCost() { return 0.0 }
virtual [string] GetDescription() { return "" }
}
class BasicCoffee : Coffee {
[double] GetCost() { return 5.0 }
[string] GetDescription() { return "Coffee" }
}
class CoffeeDecorator : Coffee {
[Coffee]$InnerCoffee
CoffeeDecorator([Coffee]$coffee) {
$this.InnerCoffee = $coffee
}
[double] GetCost() { return $this.InnerCoffee.GetCost() }
[string] GetDescription() { return $this.InnerCoffee.GetDescription() }
}
class MilkDecorator : CoffeeDecorator {
MilkDecorator([Coffee]$coffee) : base($coffee) {}
[double] GetCost() {
return $this.InnerCoffee.GetCost() + 2.0
}
[string] GetDescription() {
return $this.InnerCoffee.GetDescription() + ", Milk"
}
}
class SugarDecorator : CoffeeDecorator {
SugarDecorator([Coffee]$coffee) : base($coffee) {}
[double] GetCost() {
return $this.InnerCoffee.GetCost() + 1.0
}
[string] GetDescription() {
return $this.InnerCoffee.GetDescription() + ", Sugar"
}
}
class CaramelDecorator : CoffeeDecorator {
CaramelDecorator([Coffee]$coffee) : base($coffee) {}
[double] GetCost() {
return $this.InnerCoffee.GetCost() + 2.5
}
[string] GetDescription() {
return $this.InnerCoffee.GetDescription() + ", Caramel"
}
}
class WhippedCreamDecorator : CoffeeDecorator {
WhippedCreamDecorator([Coffee]$coffee) : base($coffee) {}
[double] GetCost() {
return $this.InnerCoffee.GetCost() + 1.5
}
[string] GetDescription() {
return $this.InnerCoffee.GetDescription() + ", Whipped Cream"
}
}
# Usage
$coffee = [BasicCoffee]::new()
Write-Host "$($coffee.GetDescription()) (`$$($coffee.GetCost())`)"
$withMilk = [MilkDecorator]::new($coffee)
Write-Host "$($withMilk.GetDescription()) (`$$($withMilk.GetCost())`)"
$withSugar = [SugarDecorator]::new($coffee)
Write-Host "$($withSugar.GetDescription()) (`$$($withSugar.GetCost())`)"
$withMilkSugar = [SugarDecorator]::new([MilkDecorator]::new($coffee))
Write-Host "$($withMilkSugar.GetDescription()) (`$$($withMilkSugar.GetCost())`)"
$fullyDecorated = [CaramelDecorator]::new(
[WhippedCreamDecorator]::new(
[SugarDecorator]::new(
[MilkDecorator]::new($coffee)
)
)
)
Write-Host "$($fullyDecorated.GetDescription()) (`$$($fullyDecorated.GetCost())`)"Implement command pattern with execute, undo, and redo operations.
- Command:
AddCommand,SubtractCommand - History:
CommandHistory - Macro:
MacroCommand - Operations:
Execute,Undo,Redo
# Command pattern
class Command {
virtual [void] Execute() {}
virtual [void] Undo() {}
virtual [void] Redo() {}
}
class AddCommand : Command {
[ref]$Receiver
[int]$Value
AddCommand([ref]$receiver, [int]$value) {
$this.Receiver = $receiver
$this.Value = $value
}
[void] Execute() {
$this.Receiver.Value += $this.Value
}
[void] Undo() {
$this.Receiver.Value -= $this.Value
}
[void] Redo() {
$this.Execute()
}
}
class SubtractCommand : Command {
[ref]$Receiver
[int]$Value
SubtractCommand([ref]$receiver, [int]$value) {
$this.Receiver = $receiver
$this.Value = $value
}
[void] Execute() {
$this.Receiver.Value -= $this.Value
}
[void] Undo() {
$this.Receiver.Value += $this.Value
}
[void] Redo() {
$this.Execute()
}
}
class MacroCommand : Command {
[Command[]]$Commands
MacroCommand([Command[]]$commands) {
$this.Commands = $commands
}
[void] Execute() {
foreach ($cmd in $this.Commands) {
$cmd.Execute()
}
}
[void] Undo() {
for ($i = $this.Commands.Count - 1; $i -ge 0; $i--) {
$this.Commands[$i].Undo()
}
}
[void] Redo() {
$this.Execute()
}
}
class CommandHistory {
[System.Collections.ArrayList]$History = @()
[int]$Current = 0
[void] Execute([Command]$command) {
$command.Execute()
$this.History = $this.History[0..($this.Current - 1)]
$this.History.Add($command)
$this.Current++
}
[bool] Undo() {
if ($this.Current -gt 0) {
$this.Current--
$this.History[$this.Current].Undo()
return $true
}
return $false
}
[bool] Redo() {
if ($this.Current -lt $this.History.Count) {
$this.History[$this.Current].Redo()
$this.Current++
return $true
}
return $false
}
}
# Usage
$counter = 0
$history = [CommandHistory]::new()
$add5 = [AddCommand]::new([ref]$counter, 5)
$sub3 = [SubtractCommand]::new([ref]$counter, 3)
Write-Host "Initial: $counter"
$history.Execute($add5)
Write-Host "After add: $counter"
$history.Execute($sub3)
Write-Host "After sub: $counter"
$history.Undo()
Write-Host "After undo: $counter"
$history.Redo()
Write-Host "After redo: $counter"
$macro = [MacroCommand]::new(@($add5, $add5, $sub3))
$history.Execute($macro)
Write-Host "After macro: $counter"Implement memento pattern for state capture and restoration.
- Originator:
Originator - Memento:
Memento - Caretaker:
Caretaker - Undo/Redo:
Undo,Redo
# Memento pattern
class Memento {
$State
Memento($state) {
$this.State = $state
}
$GetState() {
return $this.State
}
}
class Originator {
$State
Originator($state) {
$this.State = $state
}
[Memento] Save() {
return [Memento]::new($this.State)
}
[void] Restore([Memento]$memento) {
$this.State = $memento.GetState()
}
[void] SetState($state) {
$this.State = $state
}
$GetState() {
return $this.State
}
}
class Caretaker {
[System.Collections.ArrayList]$Mementos = @()
[int]$Current = 0
[void] Save([Memento]$memento) {
$this.Mementos = $this.Mementos[0..($this.Current - 1)]
$this.Mementos.Add($memento)
$this.Current++
}
[Memento] Undo() {
if ($this.Current -gt 0) {
$this.Current--
return $this.Mementos[$this.Current]
}
return $null
}
[Memento] Redo() {
if ($this.Current -lt $this.Mementos.Count) {
$memento = $this.Mementos[$this.Current]
$this.Current++
return $memento
}
return $null
}
}
# Usage
$originator = [Originator]::new(@{value = 0})
$caretaker = [Caretaker]::new()
$caretaker.Save($originator.Save())
$originator.SetState(@{value = 1})
$caretaker.Save($originator.Save())
$originator.SetState(@{value = 2})
$caretaker.Save($originator.Save())
$originator.SetState(@{value = 3})
Write-Host "Current: $($originator.GetState().value)"
$memento = $caretaker.Undo()
if ($memento) {
$originator.Restore($memento)
Write-Host "After undo: $($originator.GetState().value)"
}
$memento = $caretaker.Redo()
if ($memento) {
$originator.Restore($memento)
Write-Host "After redo: $($originator.GetState().value)"
}Implement mediator pattern for centralized communication between colleagues.
- Mediator:
Mediator - Colleague:
Colleague - Send:
Sendmethod - Register:
Registermethod
# Mediator pattern
class Mediator {
[System.Collections.ArrayList]$Colleagues = @()
[void] Register([Colleague]$colleague) {
$this.Colleagues.Add($colleague)
$colleague.SetMediator($this)
}
[void] Send([string]$message, [Colleague]$sender) {
foreach ($colleague in $this.Colleagues) {
if ($colleague -ne $sender) {
$colleague.Receive($message)
}
}
}
}
class Colleague {
[string]$Name
[Mediator]$Mediator
Colleague([string]$name) {
$this.Name = $name
}
[void] SetMediator([Mediator]$mediator) {
$this.Mediator = $mediator
}
[void] Send([string]$message) {
$this.Mediator.Send($message, $this)
}
[void] Receive([string]$message) {
Write-Host "$($this.Name) received: $message"
}
}
class StatefulColleague : Colleague {
$State
StatefulColleague([string]$name, $state) : base($name) {
$this.State = $state
}
[void] Receive([string]$message) {
Write-Host "$($this.Name) (state $($this.State)) received: $message"
}
[void] SetState($state) {
$this.State = $state
}
}
# Usage
$mediator = [Mediator]::new()
$alice = [Colleague]::new("Alice")
$bob = [Colleague]::new("Bob")
$charlie = [Colleague]::new("Charlie")
$mediator.Register($alice)
$mediator.Register($bob)
$mediator.Register($charlie)
Write-Host "Sending messages:"
$alice.Send("Hello everyone!")
$bob.Send("Meeting at 3pm")
$mediator2 = [Mediator]::new()
$alice2 = [StatefulColleague]::new("Alice", 0)
$bob2 = [StatefulColleague]::new("Bob", 1)
$mediator2.Register($alice2)
$mediator2.Register($bob2)
$alice2.Send("Custom message for stateful colleagues")Implement chain of responsibility with linked handlers.
- Handler:
Handlerclass - Chain:
SetNextmethod - Processing:
Handlemethod - Concrete:
AuthHandler,LoggerHandler
# Chain of Responsibility
class Handler {
[Handler]$Next = $null
[Handler] SetNext([Handler]$handler) {
$this.Next = $handler
return $handler
}
virtual [bool] Handle($request) {
if ($this.Next) {
return $this.Next.Handle($request)
}
return $true
}
}
class AuthHandler : Handler {
[bool] Handle($request) {
if ($request.ContainsKey("token")) {
Write-Host "Authentication passed"
return $this.Next.Handle($request)
}
Write-Host "Authentication failed"
return $false
}
}
class LoggerHandler : Handler {
[bool] Handle($request) {
$url = $request["url"] ?? "unknown"
Write-Host "Logging request: $url"
return $this.Next.Handle($request)
}
}
class ValidationHandler : Handler {
[bool] Handle($request) {
if ($request.ContainsKey("data")) {
Write-Host "Validation passed"
return $this.Next.Handle($request)
}
Write-Host "Validation failed"
return $false
}
}
class RateLimitHandler : Handler {
$LastCall = 0
$Limit = 5
[bool] Handle($request) {
$now = [DateTime]::UtcNow.Ticks / 10000000.0
if ($now - $this.LastCall -ge $this.Limit) {
$this.LastCall = $now
Write-Host "Rate limit passed"
return $this.Next.Handle($request)
}
Write-Host "Rate limit exceeded"
return $false
}
}
# Usage
$auth = [AuthHandler]::new()
$logger = [LoggerHandler]::new()
$validator = [ValidationHandler]::new()
$rateLimiter = [RateLimitHandler]::new()
$auth.SetNext($logger).SetNext($validator).SetNext($rateLimiter)
$request = @{
token = "valid"
url = "/api"
data = "payload"
}
Write-Host "Processing valid request:"
$auth.Handle($request)
$request2 = @{
url = "/public"
}
Write-Host "Processing invalid request:"
$auth.Handle($request2)Implement state pattern with context and state transitions.
- State:
Stateclass - Context:
Context - Transitions:
Handlemethod - Data:
StatefulContext
# State pattern
class State {
virtual [void] Handle([Context]$context) {}
}
class ReadyState : State {
[void] Handle([Context]$context) {
Write-Host "Ready: Waiting for input"
$context.SetState([ProcessingState]::new())
}
}
class ProcessingState : State {
[void] Handle([Context]$context) {
Write-Host "Processing: Working on task"
$context.SetState([CompletedState]::new())
}
}
class CompletedState : State {
[void] Handle([Context]$context) {
Write-Host "Completed: Task finished"
$context.SetState([ReadyState]::new())
}
}
class ErrorState : State {
[void] Handle([Context]$context) {
Write-Host "Error: Something went wrong"
$context.SetState([ReadyState]::new())
}
}
class Context {
[State]$State
[hashtable]$Data = @{}
Context([State]$state) {
$this.State = $state
}
[void] SetState([State]$state) {
$this.State = $state
}
[void] Request() {
$this.State.Handle($this)
}
[void] SetData($key, $value) {
$this.Data[$key] = $value
}
$GetData($key) {
return $this.Data[$key] ?? $null
}
}
class StatefulContext : Context {
[void] Request() {
$this.State.Handle($this)
$this.SetData("last_state", $this.State.GetType().Name)
}
}
# Usage
$context = [Context]::new([ReadyState]::new())
for ($i = 0; $i -lt 5; $i++) {
Write-Host "Step $($i + 1): " -NoNewline
$context.Request()
}
Write-Host "With data:"
$context2 = [StatefulContext]::new([ReadyState]::new())
for ($i = 0; $i -lt 5; $i++) {
$context2.SetData("step", $i + 1)
$context2.Request()
Write-Host "Data: $($context2.GetData("last_state"))"
}Implement proxy pattern for access control and lazy initialization.
- Subject:
RealSubject - Proxy:
Proxy - Logging:
LoggingProxy - Auth:
AuthProxy
# Proxy pattern
class RealSubject {
[string] Request() {
return "RealSubject: Handling request"
}
}
class Proxy {
[RealSubject]$RealSubject = $null
[string] Request() {
if ($this.RealSubject -eq $null) {
Write-Host "Proxy: Creating real subject"
$this.RealSubject = [RealSubject]::new()
}
Write-Host "Proxy: Using cached real subject"
return $this.RealSubject.Request()
}
}
class LoggingProxy {
[RealSubject]$Subject
LoggingProxy([RealSubject]$subject) {
$this.Subject = $subject
}
[string] Request() {
Write-Host "Logging: Request started"
$result = $this.Subject.Request()
Write-Host "Logging: Request completed"
return $result
}
}
class AuthProxy {
[RealSubject]$Subject
[string]$User
AuthProxy([RealSubject]$subject, [string]$user) {
$this.Subject = $subject
$this.User = $user
}
[string] Request() {
if ($this.Authenticate()) {
Write-Host "Auth: Access granted"
return $this.Subject.Request()
}
Write-Host "Auth: Access denied"
return "Unauthorized"
}
[bool] Authenticate() {
return $this.User -eq "admin"
}
}
# Usage
$proxy = [Proxy]::new()
Write-Host $proxy.Request()
Write-Host $proxy.Request()
$real = [RealSubject]::new()
$loggingProxy = [LoggingProxy]::new($real)
Write-Host $loggingProxy.Request()
$authProxy = [AuthProxy]::new($real, "admin")
Write-Host $authProxy.Request()
$authProxy2 = [AuthProxy]::new($real, "guest")
Write-Host $authProxy2.Request()Implement flyweight pattern for sharing objects to save memory.
- Flyweight:
Flyweight - Factory:
FlyweightFactory - Get:
GetFlyweight - Operation:
Operation
# Flyweight pattern
class Flyweight {
[string]$SharedState
Flyweight([string]$sharedState) {
$this.SharedState = $sharedState
}
[string] Operation([string]$uniqueState) {
return "Shared: $($this.SharedState), Unique: $uniqueState"
}
}
class FlyweightFactory {
[hashtable]$Flyweights = @{}
[Flyweight] GetFlyweight([string]$sharedState) {
if (-not $this.Flyweights.ContainsKey($sharedState)) {
$this.Flyweights[$sharedState] = [Flyweight]::new($sharedState)
}
return $this.Flyweights[$sharedState]
}
[int] GetCount() {
return $this.Flyweights.Count
}
}
# Usage
$factory = [FlyweightFactory]::new()
$fw1 = $factory.GetFlyweight("state1")
$fw2 = $factory.GetFlyweight("state1")
$fw3 = $factory.GetFlyweight("state2")
Write-Host "fw1 and fw2 are same: $($fw1 -eq $fw2)"
Write-Host "fw1 and fw3 are same: $($fw1 -eq $fw3)"
Write-Host $fw1.Operation("unique1")
Write-Host $fw2.Operation("unique2")
Write-Host $fw3.Operation("unique3")
Write-Host "Number of flyweights: $($factory.GetCount())"Implement bridge pattern for separating abstraction from implementation.
- Implementation:
ConcreteImplementationA - Abstraction:
ExtendedAbstraction - Alternative:
AlternativeAbstraction - Operation:
Operation
# Bridge pattern
class Implementation {
virtual [string] Operation() { return "" }
}
class ConcreteImplementationA : Implementation {
[string] Operation() {
return "ConcreteImplementationA: Operation"
}
}
class ConcreteImplementationB : Implementation {
[string] Operation() {
return "ConcreteImplementationB: Operation"
}
}
class Abstraction {
[Implementation]$Implementation
Abstraction([Implementation]$implementation) {
$this.Implementation = $implementation
}
virtual [string] Operation() {
return $this.Implementation.Operation()
}
}
class ExtendedAbstraction : Abstraction {
ExtendedAbstraction([Implementation]$implementation) : base($implementation) {}
[string] Operation() {
return "ExtendedAbstraction: $($this.Implementation.Operation())"
}
}
class AlternativeAbstraction : Abstraction {
AlternativeAbstraction([Implementation]$implementation) : base($implementation) {}
[string] Operation() {
return "AlternativeAbstraction: $($this.Implementation.Operation())"
}
}
# Usage
$implA = [ConcreteImplementationA]::new()
$implB = [ConcreteImplementationB]::new()
$abstraction1 = [ExtendedAbstraction]::new($implA)
$abstraction2 = [ExtendedAbstraction]::new($implB)
$abstraction3 = [AlternativeAbstraction]::new($implA)
Write-Host $abstraction1.Operation()
Write-Host $abstraction2.Operation()
Write-Host $abstraction3.Operation()Implement adapter pattern for converting interfaces.
- Target:
Target - Adaptee:
Adaptee - Adapter:
Adapter - Logging:
LoggingAdapter
# Adapter pattern
class Target {
[string] Request() {
return "Target: Request"
}
}
class Adaptee {
[string] SpecificRequest() {
return "Adaptee: Specific Request"
}
}
class Adapter : Target {
[Adaptee]$Adaptee
Adapter([Adaptee]$adaptee) {
$this.Adaptee = $adaptee
}
[string] Request() {
return $this.Adaptee.SpecificRequest()
}
}
class LoggingAdapter : Adapter {
LoggingAdapter([Adaptee]$adaptee) : base($adaptee) {}
[string] Request() {
Write-Host "Adapter: Logging request"
return $this.Adaptee.SpecificRequest()
}
}
# Usage
$target = [Target]::new()
$adaptee = [Adaptee]::new()
$adapter = [Adapter]::new($adaptee)
Write-Host $target.Request()
Write-Host $adapter.Request()
$loggingAdapter = [LoggingAdapter]::new($adaptee)
Write-Host $loggingAdapter.Request()Implement facade pattern for simplifying complex subsystems.
- Subsystems:
SubsystemA,SubsystemB - Facade:
Facade - Operations:
SimpleOperation,ComplexOperation - Interface: Simplified API
# Facade pattern
class SubsystemA {
[string] OperationA() {
return "SubsystemA: Operation"
}
}
class SubsystemB {
[string] OperationB() {
return "SubsystemB: Operation"
}
}
class SubsystemC {
[string] OperationC() {
return "SubsystemC: Operation"
}
}
class Facade {
[SubsystemA]$SubsystemA
[SubsystemB]$SubsystemB
[SubsystemC]$SubsystemC
Facade() {
$this.SubsystemA = [SubsystemA]::new()
$this.SubsystemB = [SubsystemB]::new()
$this.SubsystemC = [SubsystemC]::new()
}
[string] SimpleOperation() {
return $this.SubsystemA.OperationA()
}
[string] ComplexOperation() {
return "$($this.SubsystemA.OperationA())`n$($this.SubsystemB.OperationB())`n$($this.SubsystemC.OperationC())"
}
}
# Usage
$facade = [Facade]::new()
Write-Host "Simple operation:`n$($facade.SimpleOperation())"
Write-Host "Complex operation:`n$($facade.ComplexOperation())"Implement composite pattern for tree structures.
- Component:
Component - Leaf:
Leaf - Composite:
Composite - Operation:
Operation
# Composite pattern
class Component {
[string]$Name
Component([string]$name) {
$this.Name = $name
}
virtual [string] Operation() { return "" }
virtual [void] Add([Component]$component) { throw "Cannot add to leaf" }
virtual [void] Remove([Component]$component) { throw "Cannot remove from leaf" }
virtual [array] GetChildren() { return @() }
}
class Leaf : Component {
Leaf([string]$name) : base($name) {}
[string] Operation() {
return "Leaf $($this.Name): Operation"
}
}
class Composite : Component {
[System.Collections.ArrayList]$Children = @()
Composite([string]$name) : base($name) {}
[string] Operation() {
$result = "Composite $($this.Name): Operation`n"
foreach ($child in $this.Children) {
$result += "$($child.Operation())`n"
}
return $result
}
[void] Add([Component]$component) {
$this.Children.Add($component)
}
[void] Remove([Component]$component) {
$this.Children.Remove($component)
}
[array] GetChildren() {
return $this.Children
}
[int] CountLeaves() {
$count = 0
foreach ($child in $this.Children) {
if ($child -is [Leaf]) {
$count++
} else {
$count += $child.CountLeaves()
}
}
return $count
}
}
# Usage
$leaf1 = [Leaf]::new("A")
$leaf2 = [Leaf]::new("B")
$leaf3 = [Leaf]::new("C")
$leaf4 = [Leaf]::new("D")
$composite1 = [Composite]::new("Comp1")
$composite1.Add($leaf1)
$composite1.Add($leaf2)
$composite2 = [Composite]::new("Comp2")
$composite2.Add($leaf3)
$composite2.Add($composite1)
$root = [Composite]::new("Root")
$root.Add($leaf4)
$root.Add($composite2)
Write-Host $root.Operation()
Write-Host "Number of leaves: $($root.CountLeaves())"Implement visitor pattern for adding operations to objects.
- Visitor:
Visitor - Element:
ElementA,ElementB - Accept:
Accept - Counting:
CountingVisitor
# Visitor pattern
class Element {
[string]$Data
Element([string]$data) {
$this.Data = $data
}
virtual [string] Accept([Visitor]$visitor) { return "" }
}
class ElementA : Element {
ElementA([string]$data) : base($data) {}
[string] Accept([Visitor]$visitor) {
return $visitor.VisitA($this)
}
}
class ElementB : Element {
ElementB([string]$data) : base($data) {}
[string] Accept([Visitor]$visitor) {
return $visitor.VisitB($this)
}
}
class Visitor {
virtual [string] VisitA([ElementA]$element) { return "" }
virtual [string] VisitB([ElementB]$element) { return "" }
}
class ConcreteVisitor : Visitor {
[string] VisitA([ElementA]$element) {
return "Visiting ElementA: $($element.Data)"
}
[string] VisitB([ElementB]$element) {
return "Visiting ElementB: $($element.Data)"
}
}
class CountingVisitor : Visitor {
[int]$CountA = 0
[int]$CountB = 0
[string] VisitA([ElementA]$element) {
$this.CountA++
return "Visiting ElementA ($($this.CountA)): $($element.Data)"
}
[string] VisitB([ElementB]$element) {
$this.CountB++
return "Visiting ElementB ($($this.CountB)): $($element.Data)"
}
}
class ExtendedVisitor : Visitor {
[string] VisitA([ElementA]$element) {
return "Extended: $($element.Data) (A)"
}
[string] VisitB([ElementB]$element) {
return "Extended: $($element.Data) (B)"
}
}
# Usage
$elements = @(
[ElementA]::new("Hello"),
[ElementB]::new("World"),
[ElementA]::new("PowerShell"),
[ElementB]::new("Visitor")
)
$visitor = [ConcreteVisitor]::new()
$countingVisitor = [CountingVisitor]::new()
$extendedVisitor = [ExtendedVisitor]::new()
Write-Host "Using standard visitor:"
foreach ($element in $elements) {
Write-Host $element.Accept($visitor)
}
Write-Host "Using counting visitor:"
foreach ($element in $elements) {
Write-Host $element.Accept($countingVisitor)
}
Write-Host "Counts: A=$($countingVisitor.CountA), B=$($countingVisitor.CountB)"
Write-Host "Using extended visitor:"
foreach ($element in $elements) {
Write-Host $element.Accept($extendedVisitor)
}Implement iterator pattern for sequential access.
- Iterator:
Iterator - Reverse:
ReverseIterator - Filter:
FilteredIterator - Skip:
SkipIterator
# Iterator pattern
class Iterator {
$Collection
[int]$Position = 0
Iterator($collection) {
$this.Collection = $collection
}
$Current() {
return $this.Collection[$this.Position] ?? $null
}
[int] Key() {
return $this.Position
}
[void] Next() {
$this.Position++
}
[void] Rewind() {
$this.Position = 0
}
[bool] Valid() {
return $this.Position -lt $this.Collection.Count
}
}
class ReverseIterator : Iterator {
ReverseIterator($collection) : base($collection) {
$this.Position = $collection.Count - 1
}
[void] Next() {
$this.Position--
}
[void] Rewind() {
$this.Position = $this.Collection.Count - 1
}
[bool] Valid() {
return $this.Position -ge 0
}
}
class FilteredIterator : Iterator {
[scriptblock]$Predicate
FilteredIterator($collection, [scriptblock]$predicate) : base($collection) {
$this.Predicate = $predicate
$this.Collection = $collection | Where-Object $predicate
}
}
class SkipIterator : Iterator {
SkipIterator($collection, $n) : base($collection) {
$this.Collection = $collection[$n..($collection.Count - 1)]
}
}
# Usage
$collection = @("A", "B", "C", "D", "E")
$iterator = [Iterator]::new($collection)
Write-Host "Forward iteration:"
while ($iterator.Valid()) {
Write-Host $iterator.Current() -NoNewline
Write-Host " " -NoNewline
$iterator.Next()
}
Write-Host ""
$reverseIterator = [ReverseIterator]::new($collection)
Write-Host "Reverse iteration:"
while ($reverseIterator.Valid()) {
Write-Host $reverseIterator.Current() -NoNewline
Write-Host " " -NoNewline
$reverseIterator.Next()
}
Write-Host ""
$filteredIterator = [FilteredIterator]::new($collection, { param($item) $item.Length -le 1 })
Write-Host "Filtered iteration:"
while ($filteredIterator.Valid()) {
Write-Host $filteredIterator.Current() -NoNewline
Write-Host " " -NoNewline
$filteredIterator.Next()
}
Write-Host ""Implement template method with customizable steps.
- Template:
Template - Method:
TemplateMethod - Default:
DefaultTemplate - Logging:
LoggingTemplate
# Template Method pattern
class Template {
[void] TemplateMethod() {
Write-Host $this.Step1()
Write-Host $this.Step2()
Write-Host $this.Step3()
}
virtual [string] Step1() { return "" }
virtual [string] Step2() { return "" }
virtual [string] Step3() { return "" }
}
class DefaultTemplate : Template {
[string] Step1() { return "Step 1" }
[string] Step2() { return "Step 2" }
[string] Step3() { return "Step 3" }
}
class LoggingTemplate : Template {
[Template]$InnerTemplate
LoggingTemplate([Template]$template) {
$this.InnerTemplate = $template
}
[string] Step1() {
$result = $this.InnerTemplate.Step1()
Write-Host "Logging: $result"
return $result
}
[string] Step2() {
$result = $this.InnerTemplate.Step2()
Write-Host "Logging: $result"
return $result
}
[string] Step3() {
$result = $this.InnerTemplate.Step3()
Write-Host "Logging: $result"
return $result
}
}
class DataProcessingTemplate : Template {
[string]$Data
DataProcessingTemplate([string]$data) {
$this.Data = $data
}
[string] Step1() {
return "Processing data: $($this.Data) - Step 1"
}
[string] Step2() {
return "Processing data: $($this.Data) - Step 2"
}
[string] Step3() {
return "Processing data: $($this.Data) - Step 3"
}
}
# Usage
Write-Host "Using default template:"
$default = [DefaultTemplate]::new()
$default.TemplateMethod()
Write-Host "Using logging template:"
$logging = [LoggingTemplate]::new($default)
$logging.TemplateMethod()
Write-Host "Using data processing template:"
$dataTemplate = [DataProcessingTemplate]::new("example")
$dataTemplate.TemplateMethod()Implement builder pattern for constructing complex objects.
- Builder:
Builder - Director:
Director - Product:
Product - Build:
BuildMinimal,BuildFull
# Builder pattern
class Product {
[System.Collections.ArrayList]$Parts = @()
[void] AddPart([string]$part) {
$this.Parts.Add($part)
}
[void] ListParts() {
Write-Host ($this.Parts -join ", ")
}
}
class Builder {
[Product]$Product
Builder() {
$this.Reset()
}
[void] Reset() {
$this.Product = [Product]::new()
}
[void] BuildStepA() {
$this.Product.AddPart("Part A")
}
[void] BuildStepB() {
$this.Product.AddPart("Part B")
}
[void] BuildStepC() {
$this.Product.AddPart("Part C")
}
[Product] GetResult() {
$result = $this.Product
$this.Reset()
return $result
}
}
class Director {
[Builder]$Builder
Director([Builder]$builder) {
$this.Builder = $builder
}
[void] BuildMinimal() {
$this.Builder.BuildStepA()
}
[void] BuildFull() {
$this.Builder.BuildStepA()
$this.Builder.BuildStepB()
$this.Builder.BuildStepC()
}
[void] BuildCustom([string[]]$steps) {
$this.Builder.Reset()
foreach ($step in $steps) {
switch ($step) {
"A" { $this.Builder.BuildStepA() }
"B" { $this.Builder.BuildStepB() }
"C" { $this.Builder.BuildStepC() }
}
}
}
}
# Usage
$builder = [Builder]::new()
$director = [Director]::new($builder)
Write-Host "Minimal product:"
$director.BuildMinimal()
$builder.GetResult().ListParts()
Write-Host "Full product:"
$director.BuildFull()
$builder.GetResult().ListParts()
Write-Host "Custom product:"
$builder.BuildStepC()
$builder.BuildStepA()
$builder.GetResult().ListParts()
Write-Host "Director custom:"
$director.BuildCustom(@("C", "A", "B"))
$builder.GetResult().ListParts()Implement prototype pattern for cloning objects.
- Prototype:
Prototype - Clone:
Clone - Deep clone:
DeepClone - Mutable:
MutablePrototype
# Prototype pattern
class Prototype {
$Data
Prototype($data) {
$this.Data = $data
}
[Prototype] Clone() {
return [Prototype]::new($this.Data)
}
[Prototype] DeepClone() {
return [Prototype]::new($this.DeepCopy($this.Data))
}
hidden DeepCopy($value) {
if ($value -is [array]) {
$result = @()
foreach ($item in $value) {
$result += $this.DeepCopy($item)
}
return $result
} elseif ($value -is [hashtable]) {
$result = @{}
foreach ($key in $value.Keys) {
$result[$key] = $this.DeepCopy($value[$key])
}
return $result
} elseif ($value -is [PSCustomObject]) {
$result = [PSCustomObject]@{}
foreach ($prop in $value.PSObject.Properties) {
$result | Add-Member -MemberType NoteProperty -Name $prop.Name -Value ($this.DeepCopy($prop.Value))
}
return $result
} else {
return $value
}
}
}
class MutablePrototype : Prototype {
MutablePrototype($data) : base($data) {}
[void] SetData($data) {
$this.Data = $data
}
}
# Usage
$original = [Prototype]::new(@{Name = "Original"; Value = 42})
$copy = $original.Clone()
$deepCopy = $original.DeepClone()
Write-Host "Original: $($original.Data | ConvertTo-Json)"
Write-Host "Copy: $($copy.Data | ConvertTo-Json)"
Write-Host "Deep copy: $($deepCopy.Data | ConvertTo-Json)"
$mutable = [MutablePrototype]::new(@(1, 2, 3))
Write-Host "Original data: $($mutable.Data -join ', ')"
$mutable.SetData(@(4, 5, 6))
Write-Host "Modified data: $($mutable.Data -join ', ')"
$clonedMutable = $mutable.Clone()
Write-Host "Clone data: $($clonedMutable.Data -join ', ')"