Well, I had promised myself I wasn’t going to post all of my solutions, but after getting a look at the solutions that were posted by The Scripting Guys, I was afraid that people might actually try to learn to use PowerShell based on them, and might not understand that the huge benefit of PowerShell as a language … is the pipeline. ;) So having said that …

The task in Beginner’s Event 1 was to take a “hand” of cards like 6,6,5,6,K and output a count of how many ways there are to organize the cards into pairs. Note: this wasn’t supposed to count how many pairs you had the way you would count them in poker, but rather, to calculate the possible ways of choosing two of a kind from the hand.

The Scripting Guys Solution uses a bunch of for-loops with an internal sum — and was clearly a direct translation from some other programming language like Javascript. Here’s my solution reworked to use the same array of cards and to output the text exactly the same way. I’ve wrapped it onto multiple lines for readability, but it’s really all one pipeline you would type out as one command. I’ve written it two different ways below, but they’re EXACTLY equivalent:


@(6,6,5,6,"K") | Group |
  ForEach {  $hand = $_.Groupwhile(($null,$hand = $hand) -and $hand){$hand} } | Measure-Object
 

@(6,6,5,6,"K") | Group |
  ForEach { do{ $_.Group.RemoveAt(0); $_.Group }while($_.Group) } |
  Measure-Object | % { "Total: $($_.Count)" }
 

Basically, what it does is group the cards together into sets of matching cards, and then for each group, it loops through: it takes a single card, and outputs each remaining card (the other half of the pair). If you leave off the Measure-Object part, it will actually output the symbol for the pair (eg: for a hand: 6,6,5,“K”,“K” it would output 6,K — indicating a pair of sixes and a pair of kings).

Obviously, Measure-Object calculates the count of them which is then output at the end. Technically, you can leave off the last element of the pipeline, as I did on the first one, but it won’t output exactly the same text that the scripting guys did. Now of course, if you’re just outputting the count, instead of the actual pairs, there’s really no point in putting the pairs on the pipeline, so we could reduce this even further:


@(6,5,6,6,"K") | Group | Select Count | % { while(--$_.Count){$_.Count} } | Measure-Object -Sum
 

In this version, we just output the count. The Select Count cmdlet basically passes on just the Count property of the objects which the Group cmdlet outputs — otherwise the statement --$_.Count would generate an error about the Count property being read-only. The default alias for the “ForEach-Object” cmdlet is “%” ... and of course, now we have to -Sum the output at the end, because we output (for instance) a single number “2” instead of the number 6 twice.

The huge benefit of these methods … is that they work even if you have the full card name instead of just the simplified “6” which doesn’t even tell you what card it is. You just have to adjust the Group to use an expression:


@("Six of Hearts","Six of Clubs","Seven of Spades","Six of Diamonds","King of Hearts") |
Group {$_.Split()[0]} |
ForEach {
  $hand = $_.Group;
  while(($card,$hand = $hand) -and $hand){
    $hand | % {"$card and $_"}
  }
}
 

Here, the Group-Object cmdlet is grouping based on the evaluation of a scriptblock: the built-in variable $_ means the current item, and .Split() is a method on string objects which splits them into an array of strings based on a separator character (in this case, we’re using the default, which is a space), then [0] is the first item in that array (in other words, we’re grouping based on the first word. I went back to my first example in this case, and you can see why it was written the way it was: it allows me to explicitly write to output each of the pairs!