Transforming Data with PowerShell Classes, Part 1

PowerShell-and-Data
I hope you’ve been enjoying this series on working with data in PowerShell. If you missed the first two articles (Making Data Dance with PowerShell  & Dancing on the Table with PowerShell) I encourage you to get caught up as I will be using the same data source and ideas. Remember, the point of these articles is to demonstrate techniques for working with data sources in PowerShell. It doesn’t matter what they are or where they come from. Once you have the data as a set of objects, you can manipulate it just about anyway you need. As before, to keep this fun let’s use a CSV file with movie information.

"Title","ReleaseDate","Comments","Rating"
"Jason Bourne","7/29/2016",,"PG-13"
"The Magnificent Seven","9/23/2016","","PG-13"
"Doctor Strange","11/4/2016","Marvel","PS-13"
"Fantastic Beasts and Where to Find Them","11/18/2016","Harry Potter related","PG-13"
"Rogue One","12/16/2016","Star Wars","PG-13"
"The Dark Tower","2/17/2017","Stephen King","R"
"Ghost in the Shell","3/31/2017","Sci-Fi","R"
"Spectral","8/12/2016","Supernatural thriller","PG-13"
"The Space Between Us","8/19/2016","space adventure","PG-13"
"Miss Peregrine's Home for Peculiar Children","9/30/2016","Tim Burton","PG-13"
"Arrival","11/11/2016","sci-fi","R"
"Moana","11/25/2016","Disney animated","G"
"Passengers","12/21/2016","sci-fi","PG-13"
"Assassin's Creed","12/23/2016",,"R"
"Sing","12/23/2016","animated","PG"
"John Wick: Chapter Two","2/10/2017",,"R"
"Wonder Woman","6/2/2017","comic book","PG-13"
"Justice League","11/17/2017","","PG-13"
"Transformation","10/7/2016","Syracuse film","R"
"Night of the Living Dead: Genesis","12/30/2016","horror","R"

In this article, I want to show you another way you might work with this data. In PowerShell v5, Microsoft introduced support for classes. The intention was for you to use classes to create custom Desired State Configuration resources, but you can use classes for other purposes as well. That’s what we’re going to do so you’ll need PowerShell 5.0 or later if you want to follow along.
A class is a way to describe something with code, in this case PowerShell code. The class will define an object that we can use in PowerShell. If you recall from previous articles, I added a type name to my imported data. I can do the same thing with a class. Here’s how we can begin.

Class MyUpcoming {
#definition code goes here
} #close class definition

You can call your class anything you want, but keep it simple with no spaces or funky characters. I’m going to define an object called MyUpcoming. When you define a class, you need to define at least a set of properties. I want my class to have properties that correspond to the CSV headings as well as a few properties that I define on the fly. In previous articles, I used techniques like Add-Member to define them. In the class, I’ll go ahead and list the property names, specifying a type for each one. It looks like parameters in a function but without the commas. You can even set default values.

Class MyUpcoming {
#properties
[string]$Title
[datetime]$ReleaseDate
[string]$Comments
[int]$OpensIn
[string]$Rating
[boolean]$NowPlaying = $False
} #close class definition

At this point, I have the bare minimum for a class. I’m using the PowerShell ISE so I can select this class and press F8 to load it into my session. To create an instance of this class, you can use the built-in New() method. This is known as the class constructor.

Testing the class
Testing the class (Image Credit: Jeff Hicks)

You can also use the New-Object cmdlet.
Using the class type with New-Object
Using the class type with New-Object (Image Credit: Jeff Hicks)

You’ll see that Get-Member shows the properties I defined.

Now that I have an object, I need a way to create an instance using my data. To achieve this I can create an additional constructor.

Class MyUpcoming {
#properties
[string]$Title
[datetime]$ReleaseDate
[string]$Comments
[int]$OpensIn
[string]$Rating
[boolean]$NowPlaying = $False
#constructor
MyUpcoming([string]$Title,[datetime]$ReleaseDate,[string]$Rating,[string]$Comments) {
    $this.Title = $Title
    $this.ReleaseDate = $ReleaseDate
    $this.Rating = $Rating
    $this.Comments = $Comments
}
} #close class definition

To create a constructor, you need to specify what type of object you are creating. This will be the class name. Next, you define as many parameters as you want for the constructor method. These are positional. The parameter names do not need to match your property names, but they should be easy to understand. You’ll see why in a moment. Finally, you have a scriptblock that contains the necessary code to define an instance of the class. Use the $this variable to reference the instance you are creating. In this code, I am assigning values to the class properties from the constructor parameters. Let’s see what I have so far.
I’ll select this new definition and press F8 to load. Now I can look at the New() constructor.

Viewing the new constructor
Viewing the new constructor (Image Credit: Jeff Hicks)

You can see why I suggested you use meaningful parameter names. Let’s test it.
Creating an sample instance
Creating an sample instance (Image Credit: Jeff Hicks)

You can also use New-Object, just remember that arguments are positional.
Testing with New-Object
Testing with New-Object (Image Credit: Jeff Hicks)

Note that I have to specify something for all parameters even if it is $null or an empty string. But what about the OpensIn property? Or NowPlaying? How are those calculated?
I could have added code in the constructor to calculate these values but then they would be static. So instead, I decided to create a method for the class. When you define a class then you will follow this template:

[returntype] <MethodName> ([parameters]) {
    #PowerShell code
    #use $this to refence the current instance
    Return <some value or object>
}

If you don’t want your method to write anything to the pipeline then use [void] as the return type. In my class, I am going to create a method called Update() that doesn’t need any parameters and I want it to write a MyUpcoming object to the pipeline. Once the method is defined, I can call it in the constructor. Here’s the revised class.

Class MyUpcoming {
#properties
[string]$Title
[datetime]$ReleaseDate
[string]$Comments
[int]$OpensIn
[string]$Rating
[boolean]$NowPlaying = $False
#methods
[MyUpcoming]Update() {
    $this.OpensIn = ($this.ReleaseDate - (Get-Date)).TotalDays
    if ((Get-Date) -ge $this.ReleaseDate ) {
        $this.NowPlaying = $True
    }
    return $this
}
#constructor
MyUpcoming([string]$Title,[datetime]$ReleaseDate,[string]$Rating,[string]$Comments) {
    $this.Title = $Title
    $this.ReleaseDate = $ReleaseDate
    $this.Rating = $Rating
    $this.Comments = $Comments
    $this.Update() #<---Added
}
} #close class definition

The Update() method sets the value for the OpensIn and NowPlaying properties. Let’s re-test from the console after loading this new version.

Testing the class method
Testing the class method (Image Credit: Jeff Hicks)

I used different release dates to verify everything works as expected.

At this point I have an object class that has everything I need to work with my data. Now I need to get my data into this class. But I think I’ve given you enough for one article to think about so I’ll save that for next article, which you can read here: Data Transformations with PowerShell Classes, Part 2.