Split hastable at key/value pair - powershell

How can I split a hashtable starting from a specific key/value pair?
I have hashtable like the following, just longer:
Name Value
---- -----
Name Alpha
Age 2
Position Trick
Start date 01-01-31
End date Unknown
Name Corax
Age 21
Position Sneak
Earnings 40'000
End Date Unknown
Name Horus
Age 22
Position Dead
Why Heresy
End date 03-30-30
I tried Group-Object but it failed.
I particularly wanted to separate it by Name and everything aside from Name, Age and Position are not consistent.
My actual issue is that I want to parse the hashtable for the Name and Age when Why = Heresy, and unfortunately, the original source of the data is a list of strings, which is the reason why I convert it to a hashtable.

Hashtables are not ordered, so you can't rely on a concept of "before" and "after". If you know the specific names of one complete set of keys, then you can loop through the hashtable and build two new ones, so if you wanted one hashtable to contain the Name, Age, and Position, and the other to contain everything else, you can do something like this:
$New1 = #{}
$New2 = #{}
$KeysGroup1 = #('Name','Age','Position') # This could just be one value
$MyHashTable.GetEnumerator().ForEach({
if ($_.Key -in $KeysGroup1) {
$New1[$_.Key] = $_.Value
} else {
$New2[$_.Key] = $_.Value
}
})
You can use an ordered dictionary if order is important for you. You can use a shortcut to create a literal ordered dictionary by preceding a literal hashtable with the [ordered] type accelerator:
$myOrdered = [ordered]#{ a = 1; z = 5; g = 2 }
From there you could do a similar approach to above that relies on order.
To create an ordered dictionary that isn't based on a literal:
$myOrdered = New-Object System.Collections.Specialized.OrderedDictionary
# or in PowerShell v5
$myOrdered = [System.Collections.Specialized.OrderedDictionary]::new()
$myOrdered.Add('key','value')
Edit based on comments
It sounds more like what you have is an array of hashtables and you now want to go about filtering these.
A [hashtable] can (and is often) used as a sort of proto-object, and it can be very useful for that because it often supports the same syntax, and it has built-in literal support.
But you're starting to run into their limits, and at this point I think you want to be dealing with an array of objects and not an array of hashtables.
Luckily, there are really easy ways to create objects in PowerShell right from a hashtable:
$obj = New-Object -TypeName PSObject -Property $myHash
$obj = [PSCustomObject]$myHash
$objArray = $myHashArray.ForEach({[PSCustomObject]$myHash})
Once you've got your array of objects, the real fun begins:
$heretics = $objArray.Where({$_.Why -eq 'Heresy'})
You'll notice I didn't even bother filtering out the other properties here. You shouldn't, until you really need to. Then you can use Select-Object or just access the properties you need. So for display purposes you might just do:
$heretics | Format-Table Name,Age
There's more stuff you can do with an object that you can't with hashtables, like add special types of properties:
$objArray | Add-Member -MemberType ScriptProperty -Name IsHeretic -Value { $this.Why -eq 'Heresy' } -Force
$heretics = $objArray.Where({$_.IsHeretic})

So, in the end; I figured something out, Thanks a lot to #briantist for guidance to the right direction.
My solution code is:
$startobj = (0..($hastable.count -1)) | where {$hashtable[$_].keys -like "*Name*"}
$endobj = (0..($hashtable.count -1)) | where {$hashtable[$_].keys -like "*End date*"}
$objindex = (0..($hashtable.count -1))
$numberofobjindex = 0..($hashtable.Where({$_.keys -like "*Name*"}).count - 1)
$hashtableparsed = foreach ($numba in $numberofobjindex) {
$Conv2data = $hashtable[($startobj[$numba]..$endobj[$numba])]
[PSCustomObject] #{
Name = $Conv2data.Name
Age = $Conv2data.Age
Why = $Conv2data.Why
}
}
$hashtableparsed
I realized that the hashtable data I was presented with had a repeating pattern with Name at the start of each cycle, and End date at the end.
So I basically did an indexing of the hashtable, and marked and indexed all instances where the cycle would start and end.
I then counted the cycles, and FOR EACH cycle captured the data of hashtable data in the lines particularly of that cycle and turned it into a PSCustomObject

Related

PowerShell Create a series in System.Object (PSCustomObject)

Is there an easier way to accomplish this without typing the series out? I tried some ideas with arrays and dereferencing, but nothing ended up working.
$rowP = "" | Select-Object Course,"5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35"
I prefer the following approach.
Create an ordered dictionary:
$properties = [ordered]#{
Course = $null
}
Use the .. range operator to generate the range of integers 5 through 35, add empty entries to the dictionary:
5..35 |ForEach-Object {
$properties["${_}"] = $null
}
Convert the dictionary to a PSCustomObject:
$object = [pscustomobject]$properties
Alternatively, you could also use the range operator to generate the integer range and pass those as property names to Select-Object:
"" |Select-Object -Property #('Course'; 5..35 -as [string[]])

Weird CSV Output [duplicate]

I'm running the Test-AdfsServerHealth (Ref.)
The problem is, one of the output values (value name Output) is an array that shows up as System.Collection.Hashtable and I'm trying to find a way to get this in a neat Excel format.
For instance, this is one of the actual values on the CSV when I export:
Name Result Detail Output
TestServiceAccountProperties Pass "" System.Collections.Hashtable
But PowerShell displays:
Name : TestServiceAccountProperties
Result : Pass
Detail :
Output : {AdfsServiceAccount, AdfsServiceAccountDisabled, AdfsServiceAccountLockedOut,
AdfsServiceAccountPwdExpired...}
ExceptionMessage :
The actual command I'm running is:
$ServerResult = Test-AdfsServerHealth
tl;dr:
Test-AdfsServerHealth |
Select-Object Name, Result, Detail, #{
n='Output'
e={ $_.prop2.GetEnumerator().ForEach({ '{0}={1}' -f $_.Key, $_.Value }) -join ' ' }
} | ExportTo-Csv out.csv
The above serializes each .Output hashtable's entries into single-line string composed of space-separated <key>=<value> pairs (PSv4+ syntax) that should work reasonably well in CSV output.
Since CSV is a text format, PowerShell serializes objects to be exported by calling their .ToString() method.
Complex objects such as [hashtable] instances often yield just their full type name (System.Collections.Hashtable) for .ToString(), which isn't useful in a CSV.
A simplified example (I'm using ConvertTo-Csv, but the example applies analogously to Export-Csv):
# Create a custom object whose .col2 property is a hashtable with 2
# sample entries and convert it to CSV
PS> [pscustomobject] #{ prop1 = 1; Output = #{ name='foo'; ID=666 } } | ConvertTo-Csv
"prop1","Output"
"1","System.Collections.Hashtable"
If all output objects from Test-AdfsServerHealth had the same hashtable structure in their .Output property, you could try to flatten the hashtable by making its entries columns in their own right, but it sounds like that is not the case.
You must therefore manually transform the hashtable into a text representation that fits into a single CSV column:
You can do this with Select-Object and a calculated property that performs the transformation for you, but you need to decide on a text representation that makes sense in the context of a CSV file.
In the following example, a single-line string composed of space-separated <key>=<value> pairs is created (PSv4+ syntax).
[pscustomobject] #{ prop1 = 1; Output = #{ name='foo'; ID=666 } } |
Select-Object prop1, #{
n='Output'
e={ $_.prop2.GetEnumerator().ForEach({ '{0}={1}' -f $_.Key, $_.Value }) -join ' ' }
} | ConvertTo-Csv
For an explanation of the hashtable format that creates the calculated prop2 property, see this answer of mine.
The above yields:
"prop1","prop2"
"1","ID=666 name=foo"
Note, however, that if the values in your hashtables are again complex objects that serialize to their type name only, you'd have to apply the approach recursively.
Optional reading: Flattening a hashtable property into individual columns
If the hashtable-valued properties of the objects to export to a CSV file all have the same structure, you can opt to make the hashtable entries each their own output column.
Let's take the following sample input: a collection of 2 custom objects whose .prop2 value is a hashtable with a uniform set of keys (entries):
$coll = [pscustomobject] #{ prop1 = 1; prop2 = #{ name='foo1'; ID=666 } },
[pscustomobject] #{ prop1 = 2; prop2 = #{ name='foo2'; ID=667 } }
If you know the key names (of interest) up front, you can simply use an explicit list of calculated properties to create the individual columns:
$coll | select prop1, #{ n='name'; e={ $_.prop2.name } }, #{ n='ID'; e={ $_.prop2.ID } } |
ConvertTo-Csv
The above yields the following, showing that the hashtable entries became their own columns, name and ID:
"prop1","name","ID"
"1","foo1","666"
"2","foo2","667"
More advanced techniques are required if you do not know the key names up front:
# Create the list of calculated properties dynamically, from the 1st input
# object's .prop2 hashtable.
$propList = foreach ($key in $coll[0].prop2.Keys) {
# The script block for the calculated property must be created from a
# *string* in this case, so we can "bake" the key name into it.
#{ n=$key; e=[scriptblock]::Create("`$_.prop2.$key") }
}
$coll | Select-Object (, 'prop1' + $propList) | ConvertTo-Csv
This yields the same output as the previous command with the fixed list of calculated properties.
This won't be significantly difficult, just going to be annoying to do. The reason you are getting "System.collections.hashtable" is because is unable to display everything in that property in a single format like that, there is way to much information. You will have to create another object and put whatever information you want in there.
This prob won't work exactly like you want, but with some tweaking it should get you there.
$ServerResult = Test-ADFSServerHealth
$Object = New-PSObject -Property #{
'Name' = $ServerResult.name
'Result' = $ServerResult.Result
'Detail' = $ServerResult.Detail
'Output' = ($ServerResult.Output | out-string -stream)
'ExceptionMessage' = $ServerResult.ExceptionMessage
}
If your interested, here are the resources I used to find this answer.
Converting hashtable to array of strings
https://devops-collective-inc.gitbooks.io/the-big-book-of-powershell-gotchas/content/manuscript/new-object_psobject_vs_pscustomobject.html

Export a hashtable-valued property to a CSV file

I'm running the Test-AdfsServerHealth (Ref.)
The problem is, one of the output values (value name Output) is an array that shows up as System.Collection.Hashtable and I'm trying to find a way to get this in a neat Excel format.
For instance, this is one of the actual values on the CSV when I export:
Name Result Detail Output
TestServiceAccountProperties Pass "" System.Collections.Hashtable
But PowerShell displays:
Name : TestServiceAccountProperties
Result : Pass
Detail :
Output : {AdfsServiceAccount, AdfsServiceAccountDisabled, AdfsServiceAccountLockedOut,
AdfsServiceAccountPwdExpired...}
ExceptionMessage :
The actual command I'm running is:
$ServerResult = Test-AdfsServerHealth
tl;dr:
Test-AdfsServerHealth |
Select-Object Name, Result, Detail, #{
n='Output'
e={ $_.prop2.GetEnumerator().ForEach({ '{0}={1}' -f $_.Key, $_.Value }) -join ' ' }
} | ExportTo-Csv out.csv
The above serializes each .Output hashtable's entries into single-line string composed of space-separated <key>=<value> pairs (PSv4+ syntax) that should work reasonably well in CSV output.
Since CSV is a text format, PowerShell serializes objects to be exported by calling their .ToString() method.
Complex objects such as [hashtable] instances often yield just their full type name (System.Collections.Hashtable) for .ToString(), which isn't useful in a CSV.
A simplified example (I'm using ConvertTo-Csv, but the example applies analogously to Export-Csv):
# Create a custom object whose .col2 property is a hashtable with 2
# sample entries and convert it to CSV
PS> [pscustomobject] #{ prop1 = 1; Output = #{ name='foo'; ID=666 } } | ConvertTo-Csv
"prop1","Output"
"1","System.Collections.Hashtable"
If all output objects from Test-AdfsServerHealth had the same hashtable structure in their .Output property, you could try to flatten the hashtable by making its entries columns in their own right, but it sounds like that is not the case.
You must therefore manually transform the hashtable into a text representation that fits into a single CSV column:
You can do this with Select-Object and a calculated property that performs the transformation for you, but you need to decide on a text representation that makes sense in the context of a CSV file.
In the following example, a single-line string composed of space-separated <key>=<value> pairs is created (PSv4+ syntax).
[pscustomobject] #{ prop1 = 1; Output = #{ name='foo'; ID=666 } } |
Select-Object prop1, #{
n='Output'
e={ $_.prop2.GetEnumerator().ForEach({ '{0}={1}' -f $_.Key, $_.Value }) -join ' ' }
} | ConvertTo-Csv
For an explanation of the hashtable format that creates the calculated prop2 property, see this answer of mine.
The above yields:
"prop1","prop2"
"1","ID=666 name=foo"
Note, however, that if the values in your hashtables are again complex objects that serialize to their type name only, you'd have to apply the approach recursively.
Optional reading: Flattening a hashtable property into individual columns
If the hashtable-valued properties of the objects to export to a CSV file all have the same structure, you can opt to make the hashtable entries each their own output column.
Let's take the following sample input: a collection of 2 custom objects whose .prop2 value is a hashtable with a uniform set of keys (entries):
$coll = [pscustomobject] #{ prop1 = 1; prop2 = #{ name='foo1'; ID=666 } },
[pscustomobject] #{ prop1 = 2; prop2 = #{ name='foo2'; ID=667 } }
If you know the key names (of interest) up front, you can simply use an explicit list of calculated properties to create the individual columns:
$coll | select prop1, #{ n='name'; e={ $_.prop2.name } }, #{ n='ID'; e={ $_.prop2.ID } } |
ConvertTo-Csv
The above yields the following, showing that the hashtable entries became their own columns, name and ID:
"prop1","name","ID"
"1","foo1","666"
"2","foo2","667"
More advanced techniques are required if you do not know the key names up front:
# Create the list of calculated properties dynamically, from the 1st input
# object's .prop2 hashtable.
$propList = foreach ($key in $coll[0].prop2.Keys) {
# The script block for the calculated property must be created from a
# *string* in this case, so we can "bake" the key name into it.
#{ n=$key; e=[scriptblock]::Create("`$_.prop2.$key") }
}
$coll | Select-Object (, 'prop1' + $propList) | ConvertTo-Csv
This yields the same output as the previous command with the fixed list of calculated properties.
This won't be significantly difficult, just going to be annoying to do. The reason you are getting "System.collections.hashtable" is because is unable to display everything in that property in a single format like that, there is way to much information. You will have to create another object and put whatever information you want in there.
This prob won't work exactly like you want, but with some tweaking it should get you there.
$ServerResult = Test-ADFSServerHealth
$Object = New-PSObject -Property #{
'Name' = $ServerResult.name
'Result' = $ServerResult.Result
'Detail' = $ServerResult.Detail
'Output' = ($ServerResult.Output | out-string -stream)
'ExceptionMessage' = $ServerResult.ExceptionMessage
}
If your interested, here are the resources I used to find this answer.
Converting hashtable to array of strings
https://devops-collective-inc.gitbooks.io/the-big-book-of-powershell-gotchas/content/manuscript/new-object_psobject_vs_pscustomobject.html

powershell hashtable key versus object property

I'm confused at how PowerShell treats Hashtable keys versus object properties.
Consider:
$list = #{
'one' = #{
name = 'one'
order = 80
};
'two' = #{
name = 'two'
order = 40
};
'twotwo' = #{
name = 'twotwo'
order = 40
};
'three' = #{
name = 'three'
order = 20
}
}
$list.Values|group-object { $_.order }
$list.Values|group-object -property order
The first Group-Object gives me what I expect (three groups), the second one does not (one big group). Clearly Hashtable keys are not object properties, but syntactically they are referenced in the same manner (var.name).
What is that second group-object actually doing?
What does it think the 'order' property is?
This is understandable confusion, but as you said, hashtable keys are not object properties.
It can be tempting to treat them the same (and sometimes that works), but this is a situation where it definitely won't.
And part of the reason is that you're using a different cmdlet, not the direct language semantics.
Hashtables can use dot notation for their keys but the keys are not properties, and when you use the -Property parameter of Group-Object, you are looking for properties specifically, not just "anything you can access with a dot".
The alternative form of that parameter that takes a scriptblock, as you saw, is code that will be executed and so it's whatever value that block returns that will be grouped on.
To more directly answer your questions:
What is that second group-object actually doing?
It's looking for a property (specifically) on the current object (which is a hashtable).
If you want to see what the properties on one of those objects looks like, try this:
$list.Values[0].PSObject.Properties | ft
What does it think the 'order' property is?
It doesn't think it's anything; it looks for a property with that name and if it finds one it uses that value; otherwise it uses $null.
You'll get the same result with:
$list.Values | group -Property FakeProp
or
$list.Values | group -Property { $null }
Addressing your question in the comment:
Is there any way to know "when you should" and "when you shouldn't",
Should I just defer to using script blocks, When is -Property usage
preferred over { ... }?
-Property (without a scriptblock) is preferred whenever the value you want to group is available as a direct property of the object being inspected. If it's a property of a property, or some calculated value, or a hashtable key/value, or anything else, use a scriptblock. I'll call those "complex values".
If the complex value is useful, you may want to add it as an actual property of the object itself to encapsulate it; then you can reference it directly. This example isn't really appropriate for your hashtable situation but consider objects that represent people. They have 2 properties: Name and DateOfBirth.
$people is an array of these objects, and you want to group people by age (please ignore my inaccurate age-determining code).
$people | Group-Object -Property { ([DateTime]::Now - $_.DateOfBirth).Days / 365 -as [int] }
That's ok if you never need to know the age again; of course that's unlikely and also this looks a bit messy. It should be more immediately clear that you want to "group by age".
Instead, you can add your own (calculated) Age property to the existing objects with Add-Member:
$people |
Add-Member -MemberType ScriptProperty -Name Age -Value {
([DateTime]::Now - $this.DateOfBirth).Days / 365 -as [int]
} -Force
From here on out, each object in the $people array has an Age property that is calculated based on the value of the DateOfBirth property.
Now you can make your code clearer:
$people | Group-Object -Property Age
Again this doesn't really address your hashtable issue; the truth is they don't work that well for grouping. If you're going to do a lot of grouping with them, and you don't really need hashtables, make them into objects:
$objs = $list.Values |
ForEach-Object -Process {
New-Object -TypeName PSObject -Property $_ # takes a hashtable
}
or
$objs = $list.Values |
ForEach-Object -Process {
[PSCustomObject]$_ # converts a hashtable to PSObject
}
Then:
$objs | Group-Object -Property Order

Difference between PSObject, Hashtable, and PSCustomObject

Can anybody explain the details? If I create an object using
$var = [PSObject]#{a=1;b=2;c=3}
and then I look for its type using getType() PowerShell tells me it's of type Hashtable.
When using Get-Member (alias gm) to inspect the object it's obvious that a hashtable has been created, since it has a keys and a values property. So what's the difference to a "normal" hashtable?
Also, what's the advantage of using a PSCustomObject? When creating one using something like this
$var = [PSCustomObject]#{a=1;b=2;c=3}
the only visible difference to me is the different datatype of PSCustomObject. Also instead of keys and value properties, a inspection with gm shows that now every key has been added as a NoteProperty object.
But what advantages do I have? I'm able to access my values by using its keys, just like in the hashtable. I can store more than simple key-value pairs (key-object pairs for example) in the PSCustomObject, JUST as in the hashtable. So what's the advantage? Are there any important differences?
One scenario where [PSCustomObject] is used instead of HashTable is when you need a collection of them. The following is to illustrate the difference in how they are handled:
$Hash = 1..10 | %{ #{Name="Object $_" ; Index=$_ ; Squared = $_*$_} }
$Custom = 1..10 | %{[PSCustomObject] #{Name="Object $_" ; Index=$_ ; Squared = $_*$_} }
$Hash | Format-Table -AutoSize
$Custom | Format-Table -AutoSize
$Hash | Export-Csv .\Hash.csv -NoTypeInformation
$Custom | Export-Csv .\CustomObject.csv -NoTypeInformation
Format-Table will result in the following for $Hash:
Name Value
---- -----
Name Object 1
Squared 1
Index 1
Name Object 2
Squared 4
Index 2
Name Object 3
Squared 9
...
And the following for $CustomObject:
Name Index Squared
---- ----- -------
Object 1 1 1
Object 2 2 4
Object 3 3 9
Object 4 4 16
Object 5 5 25
...
The same thing happens with Export-Csv, thus the reason to use [PSCustomObject] instead of just plain HashTable.
Say I want to create a folder. If I use a PSObject you can tell it is wrong by
looking at it
PS > [PSObject] #{Path='foo'; Type='directory'}
Name Value
---- -----
Path foo
Type directory
However the PSCustomObject looks correct
PS > [PSCustomObject] #{Path='foo'; Type='directory'}
Path Type
---- ----
foo directory
I can then pipe the object
[PSCustomObject] #{Path='foo'; Type='directory'} | New-Item
From the PSObject documentation:
Wraps an object providing alternate views of the available members and ways to extend them. Members can be methods, properties, parameterized properties, etc.
In other words, a PSObject is an object that you can add methods and properties to after you've created it.
From the "About Hash Tables" documentation:
A hash table, also known as a dictionary or associative array, is a compact data structure that stores one or more key/value pairs.
...
Hash tables are frequently used because they are very efficient for finding and retrieving data.
You can use a PSObject like a Hashtable because PowerShell allows you to add properties to PSObjects, but you shouldn't do this because you'll lose access to Hashtable specific functionality, such as the Keys and Values properties. Also, there may be performance costs and additional memory usage.
The PowerShell documentation has the following information about PSCustomObject:
Serves as a placeholder BaseObject when PSObject's constructor with no parameters is used.
This was unclear to me, but a post on a PowerShell forum from the co-author of a number of PowerShell books seems more clear:
[PSCustomObject] is a type accelerator. It constructs a PSObject, but does so in a way that results in hash table keys becoming properties. PSCustomObject isn't an object type per se – it's a process shortcut. ... PSCustomObject is a placeholder that's used when PSObject is called with no constructor parameters.
Regarding your code, #{a=1;b=2;c=3} is a Hashtable. [PSObject]#{a=1;b=2;c=3} doesn't convert the Hashtable to a PSObject or generate an error. The object remains a Hashtable. However, [PSCustomObject]#{a=1;b=2;c=3} converts the Hashtable into a PSObject. I wasn't able to find documentation stating why this happens.
If you want to convert a Hashtable into an object in order to use its keys as property names you can use one of the following lines of code:
[PSCustomObject]#{a=1;b=2;c=3}
# OR
New-Object PSObject -Property #{a=1;b=2;c=3}
# NOTE: Both have the type PSCustomObject
If you want to convert a number of Hashtables into an object where their keys are property names you can use the following code:
#{name='a';num=1},#{name='b';num=2} |
% { [PSCustomObject]$_ }
# OR
#{name='a';num=1},#{name='b';num=2} |
% { New-Object PSObject -Property $_ }
<#
Outputs:
name num
---- ---
a 1
b 2
#>
Finding documentation regarding NoteProperty was difficult. In the Add-Member documentation, there isn't any -MemberType that makes sense for adding object properties other than NoteProperty. The Windows PowerShell Cookbook (3rd Edition) defined the Noteproperty Membertype as:
A property defined by the initial value you provide
Lee, H. (2013). Windows PowerShell Cookbook. O'Reilly Media, Inc. p. 895.
One advantage I think for PSObject is that you can create custom methods with it.
For example,
$o = New-Object PSObject -Property #{
"value"=9
}
Add-Member -MemberType ScriptMethod -Name "Sqrt" -Value {
echo "the square root of $($this.value) is $([Math]::Round([Math]::Sqrt($this.value),2))"
} -inputObject $o
$o.Sqrt()
You can use this to control the sorting order of the PSObject properties (see PSObject sorting)
I think the biggest difference you'll see is the performance. Have a look at this blog post:
Combining Objects Efficiently – Use a Hash Table to Index a Collection of Objects
The author ran the following code:
$numberofobjects = 1000
$objects = (0..$numberofobjects) |% {
New-Object psobject -Property #{'Name'="object$_";'Path'="Path$_"}
}
$lookupobjects = (0..$numberofobjects) | % {
New-Object psobject -Property #{'Path'="Path$_";'Share'="Share$_"}
}
$method1 = {
foreach ($object in $objects) {
$object | Add-Member NoteProperty -Name Share -Value ($lookupobjects | ?{$_.Path -eq $object.Path} | select -First 1 -ExpandProperty share)
}
}
Measure-Command $method1 | select totalseconds
$objects = (0..$numberofobjects) | % {
New-Object psobject -Property #{'Name'="object$_";'Path'="Path$_"}
}
$lookupobjects = (0..$numberofobjects) | % {
New-Object psobject -Property #{'Path'="Path$_";'Share'="Share$_"}
}
$method2 = {
$hash = #{}
foreach ($obj in $lookupobjects) {
$hash.($obj.Path) = $obj.share
}
foreach ($object in $objects) {
$object |Add-Member NoteProperty -Name Share -Value ($hash.($object.path)).share
}
}
Measure-Command $method2 | select totalseconds
Blog author's output:
TotalSeconds
------------
167.8825285
0.7459279
His comment regarding the code results is:
You can see the difference in speed when you put it all together. The object method takes 167 seconds on my computer while the hash table method will take under a second to build the hash table and then do the lookup.
Here are some of the other, more-subtle benefits:
Custom objects default display in PowerShell 3.0
We have a bunch of templates in our Windows-PKI and we needed a script, that has to work with all active templates. We do not need to dynamically add templates or remove them.
What for me works perfect (since it is also so "natural" to read) is the following:
$templates = #(
[PSCustomObject]#{Name = 'template1'; Oid = '1.1.1.1.1'}
[PSCustomObject]#{Name = 'template2'; Oid = '2.2.2.2.2'}
[PSCustomObject]#{Name = 'template3'; Oid = '3.3.3.3.3'}
[PSCustomObject]#{Name = 'template4'; Oid = '4.4.4.4.4'}
[PSCustomObject]#{Name = 'template5'; Oid = '5.5.5.5.5'}
)
foreach ($template in $templates)
{
Write-Output $template.Name $template.Oid
}
Type-1: $PSCustomObject = [PSCustomObject] #{a=1;b=2;c=3;d=4;e=5;f=6}
Type-2: $PsObject = New-Object -TypeName PSObject -Property #{a=1;b=2;c=3;d=4;e=5;f=6}
The only difference between Type-1 & Type-2
Type-1 Property are displayed in same order as we added
Type-1 enumerates the data faster
Type-1 will not work with systems running PSv2.0 or earlier
Both Type-1 & Type-2 are of type “System.Management.Automation.PSCustomObject”
Difference between HashTable and PSCustomObject/PSObject is
You can add new methods and properties to PSCustomObject/PSObject
You can use PSCustomObject/PSObject for pipeline parameter binding using ValueFromPipelineByPropertyName as explained by Zombo
example: [PSCustomObject] #{Path='foo'; Type='directory'} | New-Item