Hey, Scripting Guy! Event 1 *Solutions* from Expert Commentators (Beginner and Advanced; 100-meter dash)

2009 Summer Scripting Games  

(Note: These solutions were written for Event 1.) 

Beginner Event 1: The 100-Meter Dash

In the 100-meter event, you will be given the finish times of our runners. You will be asked to sort them and rank the gold, silver, and bronze winners.

Guest commentator: Steven Murawski

Steven Murawski is the Director of PowerShellCommunity.Org. His podcast can be heard on mindofroot.com. He maintains a blog at usepowershell.com. Steve agreed to provide a VBScript solution and a Windows PowerShell solution for the first event.

VBScript solution

My first thought after reviewing this problem was that this should not be too difficult. Reading files and looking for patterns in data are common tasks for IT pros of all persuasions. Here is the solution written in VBScript.

BeginnerEvent1Solution.vbs

Const ForReading = 1

Set objFS = CreateObject(“Scripting.FileSystemObject”)
Set objFile = objFS.OpenTextFile(“C:Scripts100 Meter Event.txt”, ForReading)

Set myRegExp = New RegExp
myRegExp.IgnoreCase = True
myRegExp.Pattern = “^(w+?, .+?)t(.+?)s+(d+.d+)$”


Const adVarChar = 200
Const MaxCharacters = 255
Const adFldIsNullable = 32
Const adDouble = 5

Set DataList = CreateObject(“ADOR.Recordset”)
DataList.Fields.Append “Name”, adVarChar, MaxCharacters, adFldIsNullable
DataList.Fields.Append “Country”, adVarChar, MaxCharacters, adFldIsNullable
DataList.Fields.Append “Time”, adDouble, , adFldIsNullable
DataList.Open

Do Until objFile.AtEndOfStream
          strLine = objFile.ReadLine
          Set colMatches = myRegExp.Execute(strLine)
          For Each Match In colMatches
                   DataList.AddNew
                   DataList(“Name”) = Match.SubMatches(0)
                   DataList(“Country”) = Match.SubMatches(1)
                   DataList(“Time”) = Match.SubMatches(2)
                   DataList.Update
          Next
Loop

‘close file
objFile.Close

DataList.Sort = “Time”
Wscript.Echo “Gold Medal: ” & DataList.Fields.Item(“Name”) & ” ” & DataList.Fields.Item(“Country”) & ” ” & DataList.Fields.Item(“Time”)
DataList.MoveNext

Wscript.Echo “Silver Medal: ” & DataList.Fields.Item(“Name”) & ” ” & DataList.Fields.Item(“Country”) & ” ” & DataList.Fields.Item(“Time”)
DataList.MoveNext

Wscript.Echo “Bronze Medal: ” & DataList.Fields.Item(“Name”) & ” ” & DataList.Fields.Item(“Country”) & ” ” & DataList.Fields.Item(“Time”)

Windows PowerShell solution

Here is the solution written in Windows PowerShell.

BeginnerEvent1Solution.ps1

[regex]$regex = “^(?<Name>w+?, .+?)t(?<Country>.+?)s+(?<Time>d+.d+)$”
$Name = @{Name=’Name’;Expression={$_.groups[“Name”].Value}}
$Country = @{Name=’Country’;Expression={$_.groups[“Country”].Value}}
$Time = @{Name=’Time’;Expression={$_.groups[“Time”].Value}}

$medals = ‘Gold’, ‘Silver’, ‘Bronze’

$File = ‘c:scripts100 Meter Event.txt’
$finalist = get-content $file  | ForEach-Object { $regex.Match($_) } | Where-Object {$_.Success} |
          Select-Object -Property $Name, $Country, $Time | Sort-Object -Property Time | Select-Object -First $Medals.count



for ($i = 0; $i -lt $Medals.count; $i++)
{
     Write-Host “$($Medals[$i]) Medal: $($Finalist[$i].Name) $($Finalist[$i].Country) $($Finalist[$i].Time)”
}

Here is Steven Murawski’s description of the approach he took to unraveling the mysteries of the 100-meter event.

The first thing I did was take a look at the data file and look for patterns in how the data was stored there and in the 100 Meter Event.txt file. The text file contained three categories of information—Name, Country, and Time. Names were stored as “last name, first name”. After the name, there was at least one empty space and then the country. Countries contained both single word names and compound word names. After that, at least one empty space was followed by the time of the runner.

Because there was a pattern to the data, but not a unique breaking character (spaces were in the middle of the country names, there was a comma in the name but nowhere else, and some of the spacing characters were tabs and others were single spaces), I decided to use a regular expression to parse the text.

To read in the file, I created an instance of the FileSystemObject object and used that to open the text file. This is seen here:

Const ForReading = 1
Set objFS = CreateObject(“Scripting.FileSystemObject”)
Set objFile = objFS.OpenTextFile(“C:Scripts100 Meter Event.txt”, ForReading)

After opening the file, I created my regular expression object:

Set myRegExp = New RegExp
myRegExp.IgnoreCase = True
myRegExp.Pattern = “^(w+?, .+?)t(.+?)s+(d+.d+)$”

(For more information on regular expressions, check out MSDN.)

My regular expression will take the first set of letters (or numbers) up to a comma, and a second set of letters (or numbers) up to the first white space and create the first group (the name) from that. The second group (the country) will be any and all characters after the white space following the name up to the white space preceding the time. Finally, the remaining digits and period character are saved as the third group.

To store these values, I created an in-memory dataset and opened it up:

Set DataList = CreateObject(“ADOR.Recordset”)
DataList.Fields.Append “Name”, adVarChar, MaxCharacters, adFldIsNullable
DataList.Fields.Append “Country”, adVarChar, MaxCharacters, adFldIsNullable
DataList.Fields.Append “Time”, adDouble, , adFldIsNullable
DataList.Open

Next, I looped through the text file, reading each line and matching it against my regular expression. If there was a match, I added the result to the dataset:

Do Until objFile.AtEndOfStream
          strLine = objFile.ReadLine
          Set colMatches = myRegExp.Execute(strLine)
          For Each Match In colMatches
                   DataList.AddNew
                   DataList(“Name”) = Match.SubMatches(0)
                   DataList(“Country”) = Match.SubMatches(1)
                   DataList(“Time”) = Match.SubMatches(2)
                   DataList.Update
          Next
Loop

After creating the dataset, I sorted it based on time:

DataList.Sort = “Time”

Then it was just a matter of returning the top three results and displaying them on the screen:

Wscript.Echo “Gold Medal: ” & DataList.Fields.Item(“Name”) & ” ” & DataList.Fields.Item(“Country”) & ” ” & DataList.Fields.Item(“Time”)
DataList.MoveNext
Wscript.Echo “Silver Medal: ” & DataList.Fields.Item(“Name”) & ” ” & DataList.Fields.Item(“Country”) & ” ” & DataList.Fields.Item(“Time”)
DataList.MoveNext
Wscript.Echo “Bronze Medal: ” & DataList.Fields.Item(“Name”) & ” ” & DataList.Fields.Item(“Country”) & ” ” & DataList.Fields.Item(“Time”)

Running the VBScript script should give you the following:


Image of the output from running the VBScript script

 

Here is the output I obtained when I ran the Windows PowerShell script:

Image of the results of running the Windows PowerShell script

 

Advanced Event 1: The 100-Meter Dash

The 100-meter event is the shortest outdoor distance. In this event, you will be required to read a text file and determine the shortest lines of text that it contains. 

Guest commentator: Kirk Munro

Guest commentator Kirk Munro

Kirk Munro is a PowerShell Solutions Architect and Windows PowerShell MVP. He maintains the Poshoholic.com Web site and tweets on Twitter.com/poshoholic.  

Windows PowerShell solution

Sprint. That is the name of the game for me these days. Lots to do, little time to do it. When the Scripting Guys asked me to provide a solution for the Advanced division of Event 1, I thought it was very appropriate to my work because it’s all about finding the shortest paths to get the job done. In addition, I thought it fitting to provide a nice, short solution to do it too, so let’s get started. First of all, here is the solution.

AdvancedEvent1Solution.ps1

param(
          $LiteralPath = ‘.Personal Information Cards_ADV1.txt’,
          $Count = 1
)

Get-Content -LiteralPath $LiteralPath `
          | Where-Object {$_.Trim()} `
          | Sort-Object {$_.Trim().Length} `
          | Select-Object -First $Count

At first glance, the problem is straightforward: Read in the contents of a file and determining what the three shortest lines of text contain. When you look a little more closely at the file, though, you’ll quickly realize that you can’t always count on things working as you expect. Some of the lines start with white space, and other lines contain nothing but white space. I do not consider white space to be text, so white space should be ignored—we will have to remember that when writing the solution.

Now that we’ve looked at the file we’re dealing with, the next step is to break down the problem into a set of manageable tasks. For this event, I came up with the following tasks:

·       Read the contents of the text file.

·       Filter out any lines that contain nothing but whitespace.

·       Sort the remaining lines by the length of the text (remembering to strip the white space when determining the length).

·       Show the first three lines (these will be the shortest lines after they are sorted).

This is a manageable list of tasks, so now we need to figure out what Windows PowerShell offers to solve each task. Fortunately, Windows PowerShell comes with an appropriate cmdlet for each of these tasks, making things pretty easy for us. Here are the cmdlets we will be able to use:

·       Get-Content allows you to read the contents of files.

·       Where-Object allows you to filter objects.

·       Sort-Object allows you to sort objects.

·       Select-Object allows you to pick which objects you want.

The only other detail we need to know is that you can use the trim method on strings to eliminate white space and the length property on strings to determine the length. At this point, we have enough details to create our script in our favorite script editor (mine is PowerGUI). Here is what my Get-ShortestLine.ps1 script looks like:

param(
          $LiteralPath = ‘.Personal Information Cards_ADV1.txt’,
          $Count = 1
)

Get-Content -LiteralPath $LiteralPath `
          | Where-Object {$_.Trim()} `
          | Sort-Object {$_.Trim().Length} `
          | Select-Object -First $Count

I threw a few parameters into the mix to give the script some flexibility, but basically it’s as simple as piping together the cmdlets chosen for each task and using trim where appropriate to prevent white space from skewing our results. Below you can see the results when I ran my script:

Image of the results of running the script

I hope that through this solution I have given you a few Windows PowerShell tips and tricks that you can use in your own scripts.

Guest commentator: Stephanie Peters

Guest commentator Stephanie Peters

Stephanie Peters has worked for Microsoft for more than 10 years. She is a senior premier field engineer and a veteran scripting trainer. She writes a very popular blog on TechNet, Something About Scripting.

VBScript solution

I was never much of a sprinter, actually. I am more of a survey the track, determine the strategy, and work your paces kind of girl. In this event, if you were to try to sprint off writing code at the starting gun, you might find yourself in a bit of trouble. There are a few things that need to be worked out before getting out of the blocks. OK, that’s enough with the play-on-the-track theme.

Seriously, though, there were a number of steps I had to work out before I hit my stride on this one. (Oops, sorry—will not do it again.) 

The scenario sounds simple enough:

“In this event, you will be required to read the Personal Information Cards_ADV1.txt file and determine the shortest lines of text that the file contains. You will need to display to the console, the first three shortest lines.” Here is my solution to this event:

AdvancedEvent1Solution.vbs

”””””””””””””””””””””””””””””””””””’
                                                                    
‘ Adv_1.vbs                                                          
   written by Stephanie Peters, Microsoft PFE                       
   for the 2009 Summer Scripting Games 2009                          
                                                 &n