Reputation: 11
I have a powershell script where I group and store a collection of Conputernames in a hashtable. Everithing works fine so far.
$table = @{}
$pcs = "w7cl002","w7cl002","w7cl001","w7cl002","w7cl008", `
"w7lp001","w7lp001","w7cl008","w7cl004","w7lp001"
foreach ($pc in $pcs){
if ($table.Keys -notcontains $pc){
$Table.Add($pc,1)
}
else{
$occ = $table.get_item($pc) +1
$table.set_item($pc,$occ)
}
}
$table
This is what I want and what I get.
Name Value
---- -----
stflw7lp001 3
stflw7cl002 3
stflw7cl004 1
stflw7cl001 1
stflw7cl008 2
Initially, I wanted to do this by using a 2D-Array. But after 5 hours struggling and running my head against a wall, I gave up and realized it with a hash table.
I just would be interested in whether this is possible using a 2D array?
Upvotes: 1
Views: 640
Reputation: 354794
2D arrays (in the C# sense, not the Java sense) are icky in PowerShell. I tend to avoid them. There is no direct language support for them either, and you have to create them with New-Object
.
As for your code, you can achieve the same with this:
$pcs = "w7cl002","w7cl002","w7cl001","w7cl002","w7cl008",
"w7lp001","w7lp001","w7cl008","w7cl004","w7lp001"
$table = @{}
$pcs | Group-Object -NoElement | ForEach-Object { $table[$_.Name] = $_.Count }
No need for awkward loops and code that looks like an unholy combination of C# and VBScript (honestly, none of the actual working lines in your code look like PowerShell, except for the first three and the last).
If you need arrays of arrays instead of a hashtable, you can do so as well:
$result = $pcs | Group-Object -NoElement | ForEach-Object { ,($_.Name, $_.Count) }
However, you really don't want to work this way. For one, you can often get confused whether you have an array or a scalar as an item. Some operations may automatically unroll the array again, etc. IMHO it's much better to work with objects when you can. In this case, just use the result from Group-Object
directly. It has handy Name
and Count
properties that spell out exactly what they are, instead of [0]
and [1]
which are a bit more opaque.
If you need nicer property names, just project into new objects:
$pcs | group -n | % {
New-Object PSObject -Property @{
PC = $_.Name
Count = $_.Count
}
}
or
$pcs | group -n | select @{l='PC';e={$_.Name}},Count
Generally, when using PowerShell, think of your data as objects and your problem as operations on those objects. Then try to find how to make those operations happen with things the language already gives you. Manipulation of collection classes, or C-like code with lots of loops almost always looks out of place and wrong in PowerShell – usually for good reason, because the pipeline is often a better, easier, and shorter way of accomplishing the same. I can probably count the number of times I used a for
loop in PowerShell over the past few years on both hands, while I wouldn't need both hands to count the number of times I used a foreach
loop. Those are rare things to use.
Upvotes: 3
Reputation: 18940
In general, what I end up with in a situation like this is not a 2D array, but an array of tuples, or an array of custom objects. This is what import-csv gives you by default. For my work, this is a good representation of a relation, and many of the transforms I want are some combination of set ops and relational ops.
BTW, I'm only a beginner in Powershell.
Upvotes: 0