Search code examples
powershellazure-powershell

Understanding hashtable and splatting in PowerShell 5.1


I am using PowerShell 5.1.

I'm having trouble understanding hashtable and splatting. When splatting are using a hash table to do that or is it something completely different?

I have the following code:

$hashtable1 = @{}       
$hashtable1.add('PROD',@{ FirstName = 'John'; LastName = 'Smith'})

function Main() {
    $sel = $hashtable1['PROD']
    
    Function1 $sel    
    Function2 @sel
}

function Function1([hashtable] $x) {
    "Value: $($x.LastName)"
    $x.FirstName
}

function Function2([string] $firstName) {
    "Value: $($firstName)"
}

Main

enter image description here


Solution

  • There's good information in the existing answers, but let me attempt a focused summary:

    The answer to your actual question is:

    • Yes, @{ FirstName = 'John'; LastName = 'Smith' } is a hashtable too, namely in the form of a declarative hashtable literal - just like @{} is an empty hashtable literal (it constructs an instance that initially has no entries).

      • A hashtable literal consists of zero or more key-value pairs, with = separating each key from its value, and pairs being separated with ; or newlines.
      • Keys usually do not require quoting (e.g. FirstName), except if they contain special characters such as spaces or if they're provided via an expression, such as a variable reference; see this answer for details.
    • This contrasts with adding entries to a hashtable later, programmatically, as your $hashtable1.Add('PROD', ...) method call exemplifies (where PROD is the entry key, and ... is a placeholder for the entry value).

      • Note that a more convenient alternative to using the .Add() method is to use an index expression or even dot notation (property-like access), though note that it situationally either adds an entry or updates an existing one: $hashtable1['PROD'] = ... or $hashtable1.PROD = ...

    The answer to the broader question implied by your question's title:

    • PowerShell's hashtables are a kind of data structure often called a dictionary or, in other languages, associative array or map. Specifically, they are case-insensitive instances of the .NET [hashtable] (System.Collections.Hashtable) type, which is a collection of unordered key-value pair entries. Hashtables enable efficient lookup of values by their associated keys.

      • Via syntactic sugar [ordered] @{ ... }, i.e. by placing [ordered] before a hashtable literal, PowerShell offers a case-insensitive ordered dictionary that maintains the entry-definition order and allows access by positional index in addition to the usual key-based access. Such ordered hashtables are case-insensitive instances of the .NET System.Collections.Specialized.OrderedDictionary type.

        • A quick example:

          # Create an ordered hashtable (omit [ordered] for an unordered one).
          $dict = [ordered] @{ foo = 1; bar = 'two' }
          
          # All of the following return 'two'
          $dict['bar'] # key-based access
          $dict.bar    # ditto, with dot notation (property-like access)
          $dict[1]     # index-based access; the 2nd entry's value.
          
    • Splatting is an argument-passing technique that enables passing arguments indirectly, via a variable containing a data structure encoding the arguments, which is useful for dynamically constructing arguments and making calls with many arguments more readable.

      • Typically and robustly - but only when calling PowerShell commands with declared parameters - that data structure is a hashtable, whose entry keys must match the names of the target command's parameters (e.g., key Path targets parameter -Path) and whose entry values specify the value to pass.

        • In other words: This form of splatting uses a hashtable to implement passing named arguments (parameter values preceded by the name of the target parameter, such as -Path /foo in direct argument passing).

        • A quick example:

           # Define the hashtable of arguments (parameter name-value pairs)
           # Note that File = $true is equivalent to the -File switch.
           $argsHash = @{ LiteralPath = 'C:\Windows'; File = $true }
          
           # Note the use of "@" instead of "$"; equivalent to:
           #   Get-ChildItem -LiteralPath 'C:\Windows' -File
           Get-ChildItem @argsHash
          
      • Alternatively, an array may be used for splatting, comprising parameter values only, which are then passed positionally to the target command.

        • In other words: This form of splatting uses an array to implement passing positional arguments (parameter values only).

        • This form is typically only useful:

          • when calling PowerShell scripts or functions that do not formally declare parameters and access their - invariably positional - arguments via the automatic $args variable
          • when calling external programs; note that from PowerShell's perspective there's no concept of named arguments when calling external programs, as PowerShell's knows nothing about the parameter syntax in that case, and all arguments are simply placed on the process command line one by one, and it is up to the target program to interpret them as parameter names vs. values.
        • A quick example:

           # Define an array of arguments (parameter values)
           $argsArray = 'foo', 'bar'
          
           # Note the use of "@" instead of "$", though due to calling an
           # *external program* here, you may use "$" as well; equivalent to:
           #   cmd /c echo 'foo' 'bar'              
           cmd /c echo @argsArray