Author Posts

August 16, 2018 at 2:16 pm

I teach an Intro to PowerShell lesson in a larger course.  This lesson is about 3 days long and is designed to just teach basic concepts so much of the hands-on labs are contrived for that purpose.  One thing I struggle with explaining is custom objects and why they are useful.  We have several labs where students utilize hash tables.  After introducing custom objects, we have them convert some of the hash tables in previous labs to custom objects using the [pscustomobject] accelerator.  The problem I have is explaining to students why this may be better than a hash table. If anyone can help me with real-world examples or hypothetical scenarios where using a [pscustomobject] is preferred instead of just a hash table, I'd greatly appreciate it.

August 16, 2018 at 3:40 pm

It really is a matter of choice, need and expectations. See these articles and discussions to see if it provides you the needed edification.

PsCustomObjects are effectively a superset of Hashtables.  Hashtables have name-value pairs.  The names and values don't have to be strings, but there's nothing that comes close to a ScriptProperty, etc.  Still, there are some cmdlets and constructs that are available only to Hashtables, but to PsCustomObjects, and vice versa.

'blogs.msdn.microsoft.com/timid/2013/03/05/converting-pscustomobject-tofrom-hashtables'

'powershell.org/forums/topic/what-is-the-difference-between-pscustomobject-and-psobject'

'stackoverflow.com/questions/14012773/difference-between-psobject-hashtable-and-pscustomobject?noredirect=1'

'kevinmarquette.github.io/2016-11-06-powershell-hashtable-everything-you-wanted-to-know-about'

'kevinmarquette.github.io/2016-10-28-powershell-everything-you-wanted-to-know-about-pscustomobject'

 

August 16, 2018 at 7:10 pm

I don't really agree. Hash Tables have their place particularly in key lookups, but Powershell cmdlets are built to use\parse a PSObject, not a hashtable. You're not piping hashtables to other cmdlets. It is essential to understand that to leverage the pipeline that a PSObject is required. You should cover arrays, hashtables and then PSObject, which is just an array of hashtables. Maybe something like Mr. Hicks posted in teaching Powershell leveraging veggies:

#We have multiple veggies, so we use an array to hold these values
$veggies = "zucchini", "squash", "potato", "tomato"
#Oops, we forgot a veggie
$veggies += "bean"
#Tomato is a fruit
$veggies = $veggies | Where{ $_ -ne 'tomato'}

$veggies.GetType()

#Here are our veggies
$veggies

#Ok we have veggies, but that doesn't really tell
#us much about the veggies.
#Powershell uses hashtables to hold properties,
#like information about the vegetables

$veggieProps = @{
    Name = $null
    Color = $null
    Cost = $null
}

$veggieProps.Add("Farm", $null)

$veggieProps.GetType()

#Now we have defined what we need to know about veggies,
#so how do we define these properties for every veggie?
#We create a PSObject that is an array of hashtables with these properties

$veggiesObject = foreach ($veggie in $veggies) {
    $veggieProps["Name"] = $veggie
    $veggieProps["Color"] = ("Green", "Yellow", "Red" | Get-Random -Count 1) #What are we piping to get random here, an array or hashtable?
    $veggieProps["Cost"] = (1..10 | Get-Random -Count 1) #What about this one?
    $veggieProps["Farm"] = ("SmithField", "Ole McDonald" | Get-Random -Count 1)
    
    New-Object -TypeName PSObject -Property $veggieProps
}

#Now we have an object that has each veggie with properties defined
#defined differently for each object
$veggieObject


#A more practical example using WMI to combine two sources of data into a single object...

Personally, I would show New-Object and then show an accelerator.

August 16, 2018 at 7:19 pm

one important place where PSCustomObjects are required (instead of Hashtables) – pipeline binding by property value.

Hashtables don't typically have properties that you would use as a parameter.

August 16, 2018 at 7:46 pm

Thanks all for the replies.  It still seems a little cloudy to me because there are so many exceptions.  For example I can't really say that you can't use a hash table in the pipeline becuase in some cases you can i.e. $hashtable | gm  Having said that, I know it is not always the case which leads me more to postanote's reply of use it based on "need and expectations".  Interestingly enough in the veggies example Rob provided, $veggiesObject is an array of PSObject, but one could easily change that to an array of hashtables and not see any difference in the result.  It would be nice to have an example where I can say, "see this only works as a PSObject and not as a hash table."

Rob, thanks for the suggestion, we currently do teach arrays, hashtables then PSObjects in that order, but we start with [pscustomobject] accelerator.  I like your suggestion of starting with New-Object.  I think the reason we start the other way around is because it is simpler, but may be the source of confusion early on since students really don't get the why if changing a hashtable in a previous lab to a PSObject doesn't really change the output or make the script run differently.

August 16, 2018 at 7:51 pm

Mike, thanks for the reply.  I think that is what Rob was talking about with use on the pipeline.  I just didn't get the binding by property value part of it.

August 16, 2018 at 7:58 pm

Alright, I have a simple example now.  I'll share in case anyone needs or wants to add to it.

$myHash = @{Name = "Chrome"}
$myHash | Get-Process  #returns an error

$myHash = [pscustomobject]$myHash
$myHash | Get-Process  #it works

August 19, 2018 at 8:46 pm

I'll second everything Rob said. Hash tables have a purpose: they're for looking up values by a key. That's why they're often used as Dictionaries (which are a specific hash table implementation). They're _fast_ for those lookups, too, even in large sets, because of the hashes they use for tracking keys.

Custom objects are a whole other beast. They represent _structured_ data. In a hash table, the keys you have are unknown ahead of time; with an object, the _property names_ are part of a fixed (basically) schema, allowing you to quickly work with properties. Objects are the basis of everything PowerShell is and does; nearly every command produces objects or accepts them as input.

True, custom objects are a bit more fast-and-loose than a proper, class-defined object instance, because with custom objects you can make things up as you go. But they're _intended_ to provide a lightweight way of creating the same kind of structured data a class does, without all the class-i-ness overhead and extra work.

Don't think of [pscustomobject]$hash as "turning a hash table into a custom object;" that's a little facile given what's happening. A new object is being constructed; the keys of the hash table become properties, and the values of those keys become the property values. If you're converting a single hash table that way, it probably doesn't seem to offer much value.  However, that's because your examples are probably getting ahead of their value-add.

I don't teach arrays, hash tables, or custom objects as standalone things. I teach them _as a part of what they're used for_.

An array (or collection, as PowerShell treats them as essentially the same, even though under the hood they aren't) contains multiple things. Easy examples: feeding arrays of computer names to a -ComputerName parameter.

A hash table is a good way to provide simple structured data as a form of input. Good example: Format-Table, which is designed to look for specific keys in a hash table, allowing you to define complex custom tables without having to write a bunch of XML in advance. Ditto for Select-Object.

A custom object is _best_ introduced as the right way to output _anything_ from a function. Producing custom objects really has no great value until you're producing function output. YES, you can do stuff with custom objects outside of a function, but those almost always wind up being gimmicks that should have been done in some different way.

Teaching works best for the human brain when you present a problem that everyone can agree on. For example, your first function in class might output a comma-separated values file _by hand-coding the CSV stuff_. E.g., outputting straight strings, and doing all the CSV formatting yourself by concatenating strings. This is UGLY (problem 1). You then say, "but wait... what if one day we wanted an HTML table instead? Crap, we have to start coding all over." (problem 2). But wait! If we'd written our function to output objects, then we'd get CSV and HTML for free via Export-CSV and ConvertTo-HTML! Problems solved! And at the same time, you're emphasizing WHY objects are so important in PowerShell.

So while the simple example you last posted is fine, and accurate, it's not how anyone would really use the product. I try to avoid abstract examples, because they're hard for people to understand the WHY of. Instead, you walk people down a road that they'd kind of follow by instinct anyway (e.g., hand-coding CSV). Then you point out why that's a pain (single-use; switching to HTML means starting over). THEN you show the "right way" (output objects) as the solution. That "let me lead you into a problem and then show you the solution" is why the MoL series works for so many people. It doesn't front-load concepts, but instead introduces techniques as the solutions to observable problems.

I'll stop. I have a book on Instructional Design if anyone's interested 😉

August 20, 2018 at 4:54 pm

'Book on Instructional Design' | Iam-Interested

August 22, 2018 at 6:46 am

Can I ask why that book seems to have been retired from leanpub? I've heard you praise leanpub in a podcast a while back. I do see it's available on Amazon though.

August 22, 2018 at 2:31 pm

Amazon demands a 90-day exclusivity window. So for now, the book is available only there.