While you can use arrays as hashtable keys, doing so is impractical:
Update: There is a way to make arrays work as hashtable keys, but it requires nontrivial effort during construction of the hashtable - see this answer.
You'll have the use the very same array instances as both the keys and for later lookups.
- The reason is that arrays, which are instances of .NET reference types (as opposed to value types such as integers), use the default implementation of the
.GetHashCode()
method to return a hash code (as used in hashtables), and this default implementation returns a different code for each instance - even for two array instances that one would intuitively think of as "the same".
- In other words: you'll run into the same problem trying to use instances of any such .NET reference type as hashtable keys, including other collection types - unless a given type happens to have a custom
.GetHashCode()
implementation that explicitly considers distinct instances equal based on their content.
Additionally, it makes use of PowerShell's indexer syntax ([...]
) awkward, because the array instance must be nested, with the unary form of ,
, the array constructor operator. However, dot notation (property access) works as usual.
$h = @{}
# The array-valued key.
$key = 1, 2
$h[$key] = 'a'
# IMPORTANT:
# The following lookups work, but only because
# the *very same array instance* is used for the lookup.
# Nesting required so that PowerShell doesn't think that
# *multiple* keys are being looked up.
$h[, $key]
# Dot notation works normally.
$h.$key
# Does NOT work, because a *different array instance* is used.
$h.@(1,2)
A simple test for whether a given expression results in the same hashtable lookup every time and is therefore suitable as a key is to call the .GetHashCode()
method on it repeatedly; only if the same number is returned every time (in a given session) can the expression be used:
# Returns *different* numbers.
@(1, 2).GetHashCode()
@(1, 2).GetHashCode()
To inspect a given object or type for whether it is (an instance of) a .NET reference type vs. value type:
# $false is returned in both cases, confirming that the .NET array
# type is a *reference type*
@(1, 2).GetType().IsValueType
[Array].IsValueType
Workaround:
A workaround would be to use string representations of arrays, though coming up with unique (enough) ones may be a challenge.
In the simplest case, use PowerShell's string interpolation, which represents arrays as a space-separated list of the elements' (stringified) values; e.g. "$(1, 2)"
yields verbatim 1 2
:
$h = @{}
# The array to base the key on.
$array = 1, 2
# Use the *stringified* version as the key.
$h["$array"] = 'a'
# Works, because even different array instances with equal-valued
# instances of .NET primitive types stringify the same.
# '1 2'
$h["$(1, 2)"]
iRon points out that this simplistic approach can lead to ambiguity (e.g., a single '1 2'
string would result in the same key as array 1, 2
) and recommends the following instead:
a more advanced/explicit way for array keys would be:
- joining their elements with a non-printable character; e.g.
$key = $array -join [char]27
- or, for complex object array elements, serializing the array:
$key = [System.Management.Automation.PSSerializer]::Serialize($array)
Note that even the XML (string)-based serialization provided by the System.Management.Automation.PSSerializer
class (used in PowerShell remoting and background jobs for cross-process marshaling) has its limits with respect to reliably distinguishing instances, because its recursion depth is limited - see this answer for more information; you can increase the depth on demand, but doing so can result in very large string representations.
A concrete example:
using namespace System.Management.Automation
$ht = @{}
# Use serialization on an array-valued key.
$ht[[PSSerializer]::Serialize(@(1, 2))] = 'a'
# Despite using a different array instance, this
# lookup succeeds, because the serialized representation is the same.
$ht[[PSSerializer]::Serialize(@(1, 2))] # -> 'a'
$ht.$Keys
). However, the larger issue is that the lookup only works if the very same array instance that was used to create the entry is also used in the lookup, which makes use of arrays as keys impractical. – Multilingual