Solving Programming Problems with PowerShell (Problem 2)

I recently read a blog post by Santiago Valdarrama about developer programming problems. He said that any programmer should be able to solve five problems in an hour or less with a programming language of his choice. My language of choice is PowerShell, which is probably not what he had in mind when he wrote the blog post. In the last article, I demonstrated the PowerShell solution to the first problem – adding numbers in a list multiple different ways. So what about the second problem.

Problem 2: Merging Lists

The second problem is this:

Write a function that combines two lists by alternately taking elements from each list. For example: given the two lists [a, b, c] and [1, 2, 3], the function should return [a, 1, b, 2, c, 3].

This sounds simple enough, but what if the lists are different lengths? The way that I opted to solve this is that the rest of the elements should get appended to the end of the combined list. Let’s take a look at the set-up for this one:

$list1 = @( "a", "b", "c" )
$list2 = @( "1", "2", "3" )
Merge-Lists -ListA $list1 -ListB $list2

This should print out six lines – a, 1, b, 2, c, 3. The script for this is as simple as the first problem:

function Merge-Lists
{
    [CmdletBinding()]
    Param
    (
        # The First List
        [Parameter()]
        [System.Collections.ArrayList] $ListA,

        # The First List
        [Parameter()]
        [System.Collections.ArrayList] $ListB
    )

    Process
    {
        $i = 0;
        while ($i -lt $ListA.Count -and $i -lt $ListB.Count) {
            if ($i -lt $ListA.Count) {
                $ListA[$i] | Write-Output
            }
            if ($i -lt $ListB.Count) {
                $ListB[$i] | Write-Output
            }
            $i = $i + 1
        }
    }
}

This technically does the job. However, we can actually do better from a functionality basis. Let’s say I want to use a slightly different syntax. Instead of passing in the first array as an argument, maybe I want to pass it in as the pipeline. Something like:

$list1 | Merge-WithList -With $list2

In addition, I want to use parameters within the same function:

Merge-WithList -Source $list1 -With $list2

We can actually support both of these cases in one function. Here it is:

function Merge-WithList
{
    [CmdletBinding()]
    Param
    (
        # The First List
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [System.Collections.ArrayList] $Source,

        # The First List
        [Parameter()]
        [System.Collections.ArrayList] $With
    )

    Begin
    {
        $i = 0
    }
    Process
    {
        foreach ($s in $Source) {
            $s | Write-Output
            if ($i -lt $With.Count) {
                $With[$i] | Write-Output
            }
            $i += 1
        }
    }
    End
    {
        While ($i -lt $With.Count) {
            $With[$i] | Write-Output
            $i += 1
        }
    }
}

In this version we use the ValueFromPipeline to feed the array into the $Source variable. I’ve also put the initialization in the Begin block and the finalization (in case the With array is longer than the source array) in the End block.