Instead of piping over collections, it's sometimes more convenient to procedurally loop through them. And to avoid differentiating between $_
and $_.Key
/$_.Value
depending on input, a more consistent key/value handling would be nice:
ForEach-KV $object { Param($k, $v); do-stuff }
However a common type probing has its drawbacks:
#-- iterate over dicts/objects/arrays using scriptblock with Param($k,$v)
function ForEach-KV {
Param($var, $cb, $i=0)
switch ($var.GetType().Name) {
Array { $var | % { $cb.Invoke($i++, $_) } }
HashTable { $var.Keys | % { $cb.Invoke($_, $var[$_]) } }
"Dictionary``2" { $var.Keys | % { $cb.Invoke($_, $var.Item($_)) } }
PSobject { $var.GetIterator() | % { $cb.Invoke($_.Key, $_.Value) } }
PSCustomObject { $var.GetIterator() | % { $cb.Invoke($_.Key, $_.Value) } }
default { $cb.Invoke($i++, $_) }
}
}
Apart from that one irritating type name, there's a bit much duplication here. Which is why I was looking around for duck typing in Powershell.
For hashes and objects, it's easiest/obvious to probe for .getIterator
or .getEnumerator
(never couldn't quite remember which belongs to which anyway):
switch ($_) {
{ $_.GetEnumerator } { do-loopy }
{ $_.GetIterator } { do-otherloopy }
But now I'm not quite sure what to do about arrays here. There's not that one behaviour indicator in [array]
s methods that really sticks out.
.Get()
does seem unique (at least not a method in HashTables or PSObjects), but sounds a bit too generic even for type guessing.Add()
might as well be an integer method(?).GetUpperBound()
etc. come off as a bit too specific already.
So, is there a standard method that says "arrayish", preferrably something that's shared among other numerically-indexed value collections?
If you want to match only arrays:
or you can check there is integer indexer: