Search code examples
powershelldictionarytuples

Dictionary with Tuple key in Powershell


I need to create a Dictionary with Tuple key in Powershell. Exactly like I can do in C# like this:

var test = new Dictionary<(int, bool), int>();

// Add
test.Add((1, false), 5);

// Get
int a = test[(1, false)];

(Taken from Hashtable with MultiDimensional Key in C#)

Is it possible? (I am running Powershell version 5.1.18362.145.)

Thanks!


Solution

  • To add to Jeroen Mostert's excellent comments on the question:

    The following is a direct translation of your C# code to PowerShell v5.1+ code:

    using namespace System.Collections.Generic
    
    # Construct
    $test = [Dictionary[[ValueTuple[int, bool]], int]]::new()
    
    # Add
    $test.Add([ValueTuple[int, bool]]::new(1, $false), 5)
    
    # Get
    $test[[ValueTuple[int, bool]]::new(1, $false)]
    
    • using namespace is a PSv5+ feature akin to C#'s using construct: it allows you to refer to the types in the specified namespace by their mere names, without namespace qualification.

    • As Jeroen points out, PowerShell has no syntactic sugar for value-tuple instances, so that C# tuple literal (1, false) must be represented as an explicit constructor call:
      [ValueTuple[int, bool]]::new(1, $false).

      • The alternative is to use the static Create() method on the non-generic System.ValueType base type, in which case the tuple component types are inferred:
        [ValueTuple]::Create(1, $false)

    Given that PowerShell exposes a type's constructors via the static ::new() method on the type itself, you can simplify the code by instantiating the specific tuple type once and reusing it via a variable:

    using namespace System.Collections.Generic
    
    # Instantiate the concrete tuple type (type arguments locked in).
    $tupleType = [ValueTuple[int, bool]]
    
    # Construct the dictionary with the tuple type as the key.
    # See explanation below.
    $test = [Dictionary`2].MakeGenericType($tupleType, [int])::new()
    
    #`# Add
    $test.Add($tupleType::new(1, $false), 5)
    
    # Get
    $test[$tupleType::new(1, $false)]
    

    The downside is that the dictionary construction becomes more awkward, because PowerShell type literals such as [Dictionary[[ValueTuple[int, bool]], int]] must not have non-literal components. To work around that, System.Type.MakeGenericType() is used to construct the closed generic type from the dynamically specified type arguments; note the need to specify the arity (`2) of the open generic type on which .MakeGenericType() is invoked.