Quick crash course into using hash tables in PowerShell.

<#PSScriptInfo
.VERSION 1.0.0
.GUID 50fa84c4-080d-45cd-82a8-9e8dba10b187
.AUTHOR Kamil Procyszyn
.COPYRIGHT Kamil Procyszyn
.PROJECTURI https://github.com/kprocyszyn/About-PowerShell
.RELEASENOTES 2021 September
.DESCRIPTION
Link to the video: https://youtu.be/oti2l8EmAT8
Documentation: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_hash_tables?view=powershell-7.1#:~:text=In%20PowerShell%2C%20each%20hash%20table%20is%20a%20Hashtable,to%20create%20an%20ordered%20dictionary%20%28System.Collections.Specialized.OrderedDictionary%29%20in%20PowerShell.
#>

<#
About hashtable:
A hash table, also known as a dictionary or associative array, 
is a compact data structure that stores one or more key/value pairs. 
For example, a hash table might contain a series of IP addresses and computer names, 
where the IP addresses are the keys and the computer names are the values, or vice versa.
#>

#Creating a hashtable
$hash = @{}

#############################
# Adding items to hashtable #
#############################

$hash.Add("Person", "John")
$hash.Add("Email", "John@john.com")
$hash.Add("Pet", "Cat")
$hash.Add("Surname", "Smith")
$hash

# Removing Key from hashtable
$hash.Remove("Surname")
$hash

# Prepopulating hashtable
$details = @{
    Person = "Mike"
    Email = "Mike@Mike.com"
    Pet = "Dog"
}
$details

# Adding key that already exists throws
$details.Add("Pet", "Snake")

###################
# Accessing items #
###################

# Accessing with property name
$details['Pet']
$details.Pet

# We can use variable
$property = "Email"
$details[$property]

# Accessing multiple properties
$details["Email", "Person"]

# Non existent value doesn't throw
$details['Surname']
$details.Surname

# Although we can check for true/false 
[bool]$details['Surname']

###################
# Updating values #
###################

# We can update and add new values
$details['Pet'] = "Snake"
$details['Drink'] = "Flat white"
$details

$details.pet = "Dog"
$details.Food = "Lamb Shish Kebab"
$details

###########
# Looping #
###########

# using foreach
foreach ($key in $details.Keys) {
    "{0} is {1}" -f $key, $details[$key]
}

# piping to foreach-object
$details.Keys | ForEach-Object {
    "{0} is {1}" -f $_, $details[$_]
}

# GetEnumerator() allows us to work with properties
$details.GetEnumerator() | ForEach-Object {
    "{0} is {1}" -f $_.Key, $_.Value
}

# Values can't be updated while enumarated!
foreach ($key in $details.Keys) {
    $details[$key] = 'BLAH'
}

# Keys must be cloned before updating
$details.Keys.Clone() | ForEach-Object {
    $details[$_] = "SANITISED"
}
$details

######
# IF #
######

# checking if hashtable exists
if ($details) {"It's there"}

$empty = @{}
if ($empty) {"It's empty, but it's there!"}

# Checking if key exists
if ($details.Person) {"There's some info about a person"}

# Deailing with false
$details.Add("Empty", "")
$details.Add("Null", $Null)
$details.Add("False", $false)
$details

if ($details.Empty) {"It's there"}
if ($details.Null) {"It's there"}
if ($details.False) {"It's there"}

if ($details.ContainsKey('Null')) {"It's null!"}

######################
# Custom expressions #
######################

$employee = @{
    Name = "Beth"
    HourlyWage = 10
    HoursWorked = 7.5
    }
$employee | Select-Object -Property  *,@{N = "Wage"; E = {$_.HourlyWage * $_.HoursWorked } }

#############
# Splatting #
#############

Invoke-RestMethod -Uri catfact.ninja/facts -Method Get | Select-Object Data -ExpandProperty Data

$params = @{
    Uri = "catfact.ninja/facts"
    Method = "Get"
}
Invoke-RestMethod @params | Select-Object Data -ExpandProperty Data

# Adding parameters

$Verbose = $true
if ($Verbose) {
    $params['Verbose'] = $true
}
Invoke-RestMethod @params

#####################
# Nested hashtables #
#####################

$Environments = @{

    Development = @{
        Server = "Server1"
        Admin = "Rachel Green"
        Credentials = @{
            Username = "Serv1"
            Password = "Secret"
        }
    }

    Test = @{
        Server = "Server2"
        Admin = "Joe Triviani"
        Credentials = @{
            Username = "Serv2"
            Password = "Letmein"
        }
    }

}

# Accessing nested properties
$Environments
$Environments.Keys
$Environments.Values

$Environments.Test
$Environments.Test.Credentials
$Environments['Test']['Credentials']

# Updating nested properties
$Environments.Test.Credentials.Username = "admin"
$Environments.Test.Credentials

# Quickly unwrapping all properties
$Environments | ConvertTo-Json -Depth 99

# Adding nested properties
$Environments['Production'] = @{}
$Environments.Production.Server = "Server3"
$Environments.Production.Admin = "Ross Geller"
$Environments.Production['Credentials'] = @{}
$Environments.Production.Credentials.Username = "Serv3"
$Environments.Production.Credentials.Password = "Nosuchplace"

#####################
# Working with JSON #
#####################

$Environments | ConvertTo-Json | Out-File C:\temp\envs.json
Get-Content C:\Temp\envs.json | ConvertFrom-Json #This will create pscustomobject

##################
# PSCustomObject #
##################

# Creating a new object
$EnvironmentsObject = [pscustomobject]@{

    Development = @{
        Server = "Server1"
        Admin = "Rachel Green"
        Credentials = @{
            Username = "Serv1"
            Password = "Secret"
        }
    }

    Test = @{
        Server = "Server2"
        Admin = "Joe Triviani"
        Credentials = @{
            Username = "Serv2"
            Password = "Letmein"
        }
    }
}

# Casting hashtable to object
[PSCustomObject]$Environments