When creating a hash with an array key, How do i generate a key to look up the hash value.
that is, without getting it from the hash's enumerator
$a = @{"a" = "1"
"b" = "2"
("c","c1") = "3"}
Using a regular array, doesn't seem to work.
$k1 = @("c","c1")
$a.ContainsKey($k1) #false
However, if the array object is used on creation, this seems to work.
$k1 = @("c","c1")
$a = @{"a" = "1"
"b" = "2"
$k1 = "3"}
$a.ContainsKey($k1) #true
if for example, i use this to generate a hashtable:
$a = Get-Eventlog system -newest 100 | Group-Object {$_.EntryType, $_.Source } -AsHashTable
how do i generate a variable usable for key lookup myself?
Does the modification below help? It makes the keys into strings.
$a = Get-Eventlog system -newest 100 | Group-Object {
$_.EntryType, $_.Source
} -AsHashTable -AsString
Maybe you could give it a try.
Update to answer, an explanation why you couldn't use arrays for hashing the way you wanted, but strings work.
Basically, there are two things you need to know.
.NET (actually, really CLR) has the concepts of things with value semantics versus things with reference semantics. Value semantics are generally used for things with simple values, such as strings and numbers, "xyz"
, and 167
(arbitrary examples). Reference semantics are used for objects. Things with value semantics are held to be equal when their values are the same. Things with reference semantics are not equal unless they are the exact same object (located at the same address in memory).
One additional wrinkle: things with values semantics can be represented by objects (this can involve something called boxing and unboxing, but I only throw those in there to point you at future exploration--it's too much to get into for now). When objects are used to represent things with value semantics, the base class (actually a struct, I think) used is System.ValueType
, and comparing two items of System.ValueType
is a special case: even if the two items are not at the same memory address, if the two objects contain equal values, they are held to be "equal".
Take a look at the example below (comparing two ints vs two arrays), which illustrates these things. See how the ints are "equal" and the arrays are not.
$a = 167; $b = 167; echo $($a.Equals($b)); #prints True
$c = @(167,"xyz"); $d = @(167,"xyz"); echo $($c.Equals($d)); #prints False
Any .NET (well, really, CLR) object is capable of having a hash code value computed on it, and that value is what is used if you are using the object as a hash key. The function GetHashCode() will produce a hash code for the item. Example: $a = "xyz"; $a.GetHashCode();
The explanation for what you ran into
Let's put 1 and 2 above together with respect to your question. Since an array has reference semantics, comparing two array objects is comparing two different objects at two different memory locations, and they are not held to be equal. Further, this means that their hash codes will not be equal.
This, using the arrays from above, echo $c.GetHashCode(); echo $d.GetHashCode();
produces two different hash codes.
However, things with value semantics, like two value-identical strings, will actually produce the same hash code:
$e = "xyz"; $f = "xyz"; echo $e.GetHashCode(); echo $f.GetHashCode();
So, hashing by arrays caused you problems because the hash codes generated for the keys were different (unless you used the exact same array, something you correctly observed).
But hashing by, say, strings, gets around the problem, because the hash codes are the same.
Final Note
You don't need to know this to get the substance of the explanation above, but one of the things the CLR will do for you (generally) is actually make two value-identical value types, be the same object (or struct) when you are representing value types via objects. So $a = "xyz"; $b = "xyz";
will actually be made to refer to the same object, i.e., $a is the same object as $b--they are not just value identical, they are identical. This additional information does not need to clash with the simplified information above.
Is this acceptable ?
Here is my hashtable :
$a = @{"Aurillac"="rouge et bleu";"Lyon"="Jaune";"Paris"="Blanc"}
Assuming $a.keys
gives :
Paris
Aurillac
Lyon
Look for $b = "Aurillac","Lyon"
as keys of $a
is a comparaison between two arrays so :
$a.Keys | ? {$b -contains $_}
Aurillac
Lyon
or
$a.Keys | ? {$b -notcontains $_}
Paris
If you want a boolean result :
($a.Keys | ? {$b -contains $_}) -eq $null