Chapter 7. Lists, Arrays, and Hashtables

Introduction

Most scripts deal with more than one thing—lists of servers, lists of files, lookup codes, and more. To enable this, PowerShell supports many features to help you through both its language features and utility cmdlets.

PowerShell makes working with arrays and lists much like working with other data types: you can easily create an array or list and then add or remove elements from it. You can just as easily sort it, search it, or combine it with another array. When you want to store a mapping between one piece of data and another, a hashtable fulfills that need perfectly.

Create an Array or List of Items

Problem

You want to create an array or list of items.

Solution

To create an array that holds a given set of items, separate those items with commas:

PS > $myArray = 1,2,"Hello World"
PS > $myArray
1
2
Hello World

To create an array of a specific size, use the New-Object cmdlet:

PS > $myArray = New-Object string[] 10
PS > $myArray[5] = "Hello"
PS > $myArray[5]
Hello

To create an array of a specific type, use a strongly typed collection:

PS > $list = New-Object Collections.Generic.List[Int]
PS > $list.Add(10)
PS > $list.Add("Hello")
Cannot convert argument "0", with value: "Hello", for "Add" to type "System
.Int32": "Cannot convert value "Hello" to type "System.Int32". Error:
"Input string was not in a correct format.""

To store the output of a command that generates a list, use variable assignment:

PS > $myArray = Get-Process
PS > $myArray

Handles  NPM(K)    PM(K)      WS(K) VM(M)  CPU(s)     Id ProcessName
-------  ------    -----      ----- -----  ------     -- -----------
    274       6     1316       3908    33           3164 alg
    983       7     3636       7472    30            688 csrss
     69       4      924       3332    30    0.69   2232 ctfmon
    180       5     2220       6116    37           2816 dllhost
(...)

To create an array that you plan to modify frequently, use an ArrayList, as shown by Example 7-1.

Example 7-1. Using an ArrayList to manage a dynamic collection of items

PS > $myArray = New-Object System.Collections.ArrayList
PS > [void] $myArray.Add("Hello")
PS > [void] $myArray.AddRange( ("World","How","Are","You") )
PS > $myArray
Hello
World
How
Are
You
PS > $myArray.RemoveAt(1)
PS > $myArray
Hello
How
Are
You

Discussion

Aside from the primitive data types (such as strings, integers, and decimals), lists of items are a common concept in the scripts and commands that you write. Most commands generate lists of data: the Get-Content cmdlet generates a list of strings in a file, the Get-Process cmdlet generates a list of processes running on the system, and the Get-Command cmdlet generates a list of commands, just to name a few.

Note

The solution shows how to store the output of a command that generates a list. If a command outputs only one item (such as a single line from a file, a single process, or a single command), then that output is no longer a list. If you want to treat that output as a list even when it is not, use the list evaluation syntax ( @() ) to force PowerShell to interpret it as an array:

$myArray = @(Get-Process Explorer)

When you want to create a list of a specific type, the solution demonstrates how to use the System.Collections.Generic.List collection to do that. After the type name, you define the type of the list in square brackets, such as [Int], [String], or whichever type you want to restrict your collection to. These types of specialized objects are called generic objects. For more information about creating generic objects, see Creating Instances of Types.

For more information on lists and arrays in PowerShell, see Arrays and Lists.

Create a Jagged or Multidimensional Array

Problem

You want to create an array of arrays or an array of multiple dimensions.

Solution

To create an array of arrays (a jagged array), use the @() array syntax:

PS > $jagged = @(
      (1,2,3,4),
      (5,6,7,8)
   )

PS > $jagged[0][1]
2
PS > $jagged[1][3]
8

To create a (nonjagged) multidimensional array, use the New-Object cmdlet:

PS > $multidimensional = New-Object "int32[,]" 2,4
PS > $multidimensional[0,1] = 2
PS > $multidimensional[1,3] = 8
PS >
PS > $multidimensional[0,1]
2
PS > $multidimensional[1,3]
8

Discussion

Jagged and multidimensional arrays are useful for holding lists of lists and arrays of arrays. Jagged arrays are arrays of arrays, where each array has only as many elements as it needs. A nonjagged array is more like a grid or matrix, where every array needs to be the same size. Jagged arrays are much easier to work with (and use less memory), but nonjagged multidimensional arrays are sometimes useful for dealing with large grids of data.

Since a jagged array is an array of arrays, creating an item in a jagged array follows the same rules as creating an item in a regular array. If any of the arrays are single-element arrays, use the unary comma operator. For example, to create a jagged array with one nested array of one element:

PS > $oneByOneJagged = @(
 ,(,1)

PS > $oneByOneJagged[0][0]

For more information on lists and arrays in PowerShell, see Arrays and Lists.

Access Elements of an Array

Problem

You want to access the elements of an array.

Solution

To access a specific element of an array, use PowerShell’s array access mechanism:

PS > $myArray = 1,2,"Hello World"
PS > $myArray[1]
2

To access a range of array elements, use array ranges and array slicing:

PS > $myArray = 1,2,"Hello World"
PS > $myArray[1..2 + 0]
2
Hello World
1

Discussion

PowerShell’s array access mechanisms provide a convenient way to access either specific elements of an array or more complex combinations of elements in that array. In PowerShell (as with most other scripting and programming languages), the item at index 0 represents the first item in the array.

For long lists of items, knowing the index of an element can sometimes pose a problem. For a solution to this, see the Add-FormatTableIndexParameter script included with this book’s code examples. This script adds a new -IncludeIndex parameter to the Format-Table cmdlet:

PS > $items = Get-Process outlook,powershell,emacs,notepad
PS > $items

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    163       6    17660      24136   576     7.63   7136 emacs
     74       4     1252       6184    56     0.19  11820 notepad
   3262      48    46664      88280   376    20.98   8572 OUTLOOK
    285      11    31328      21952   171   613.71   4716 powershell
    767      14    56568      66032   227   104.10  11368 powershell


PS > $items | Format-Table -IncludeIndex

PSIndex Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
------- -------  ------    -----      ----- -----   ------     -- -----------
0           163       6    17660      24136   576     7.63   7136 emacs
1            74       4     1252       6184    56     0.19  11820 notepad
2          3262      48    46664      88280   376    20.98   8572 OUTLOOK
3           285      11    31328      21952   171   613.71   4716 powershell
4           767      14    56568      66032   227   104.15  11368 powershell


PS > $items[2]

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
   3262      48    46664      88280   376    20.98   8572 OUTLOOK

Although working with the elements of an array by their numerical index is helpful, you may find it useful to refer to them by something else—such as their name, or even a custom label. This type of array is known as an associative array (or hashtable). For more information about working with hashtables and associative arrays, see Create a Hashtable or Associative Array.

For more information on lists and arrays in PowerShell (including the array ranges and slicing syntax), see Arrays and Lists. For more information about obtaining the code examples for this book, see Code Examples.

Visit Each Element of an Array

Problem

You want to work with each element of an array.

Solution

To access each item in an array one by one, use the Foreach-Object cmdlet:

PS > $myArray = 1,2,3
PS > $sum = 0
PS > $myArray | Foreach-Object { $sum += $_ }
PS > $sum
6

To access each item in an array in a more script-like fashion, use the foreach scripting keyword:

PS > $myArray = 1,2,3
PS > $sum = 0
PS > foreach($element in $myArray) { $sum += $element }
PS > $sum
6

To access items in an array by position, use a for loop:

PS > $myArray = 1,2,3
PS > $sum = 0
PS > for($counter = 0; $counter -lt $myArray.Count; $counter++) {
    $sum += $myArray[$counter]
}

PS > $sum
6

Discussion

PowerShell provides three main alternatives to working with elements in an array. The Foreach-Object cmdlet and foreach scripting keyword techniques visit the items in an array one element at a time, whereas the for loop (and related looping constructs) lets you work with the items in an array in a less structured way.

For more information about the Foreach-Object cmdlet, see Work with Each Item in a List or Command Output.

For more information about the foreach scripting keyword, the for keyword, and other looping constructs, see Repeat Operations with Loops.

Sort an Array or List of Items

Problem

You want to sort the elements of an array or list.

Solution

To sort a list of items, use the Sort-Object cmdlet:

PS > Get-ChildItem | Sort-Object -Descending Length | Select Name,Length

Name                                                                Length
----                                                                ------
Convert-TextObject.ps1                                                6868
Connect-WebService.ps1                                                4178
Select-FilteredObject.ps1                                             3252
Get-PageUrls.ps1                                                      2878
Get-Characteristics.ps1                                               2515
Get-Answer.ps1                                                        1890
New-GenericObject.ps1                                                 1490
Invoke-CmdScript.ps1                                                  1313

Discussion

The Sort-Object cmdlet provides a convenient way for you to sort items by a property that you specify. If you don’t specify a property, the Sort-Object cmdlet follows the sorting rules of those items if they define any.

The Sort-Object cmdlet also supports custom sort expressions, rather than just sorting on existing properties. To sort by your own logic, use a script block as the sort expression. This example sorts by the second character:

PS > "Hello","World","And","PowerShell" | Sort-Object { $_.Substring(1,1) }
Hello
And
PowerShell
World

If you want to sort a list that you’ve saved in a variable, you can either store the results back in that variable or use the [Array]::Sort() method from the .NET Framework:

PS > $list = "Hello","World","And","PowerShell"
PS > $list = $list | Sort-Object
PS > $list
And
Hello
PowerShell
World
PS > $list = "Hello","World","And","PowerShell"
PS > [Array]::Sort($list)
PS > $list
And
Hello
PowerShell
World

In addition to sorting by a property or expression in ascending or descending order, the Sort-Object cmdlet’s -Unique switch also allows you to remove duplicates from the sorted collection.

For more information about the Sort-Object cmdlet, type Get-Help Sort-Object.

Determine Whether an Array Contains an Item

Problem

You want to determine whether an array or list contains a specific item.

Solution

To determine whether a list contains a specific item, use the -contains operator:

PS > "Hello","World" -contains "Hello"
True
PS > "Hello","World" -contains "There"
False

Discussion

The -contains operator is a useful way to quickly determine whether a list contains a specific element. To search a list for items that instead match a pattern, use the -match or -like operators.

For more information about the -contains, -match, and -like operators, see Comparison Operators.

Combine Two Arrays

Problem

You have two arrays and want to combine them into one.

Solution

To combine PowerShell arrays, use the addition operator (+):

PS > $firstArray = "Element 1","Element 2","Element 3","Element 4"
PS > $secondArray = 1,2,3,4
PS >
PS > $result = $firstArray + $secondArray
PS > $result
Element 1
Element 2
Element 3
Element 4
1
2
3
4

Discussion

One common reason to combine two arrays is when you want to add data to the end of one of the arrays. For example:

PS > $array = 1,2
PS > $array = $array + 3,4
PS > $array
1
2
3
4

You can write this more clearly as:

PS > $array = 1,2
PS > $array += 3,4
PS > $array
1
2
3
4

When this is written in the second form, however, you might think that PowerShell simply adds the items to the end of the array while keeping the array itself intact. This is not true, since arrays in PowerShell (like most other languages) stay the same length once you create them. To combine two arrays, PowerShell creates a new array large enough to hold the contents of both arrays and then copies both arrays into the destination array.

If you plan to add and remove data from an array frequently, the System.Collections.ArrayList class provides a more dynamic alternative. For more information about using the ArrayList class, see Use the ArrayList Class for Advanced Array Tasks.

Find Items in an Array That Match a Value

Problem

You have an array and want to find all elements that match a given item or term—either exactly, by pattern, or by regular expression.

Solution

To find all elements that match an item, use the -eq, -like, and -match comparison operators:

PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12"
PS > $array -eq "Item 1"
Item 1
Item 1
PS > $array -like "*1*"
Item 1
Item 1
Item 12
PS > $array -match "Item .."
Item 12

Discussion

The -eq, -like, and -match operators are useful ways to find elements in a collection that match your given term. The -eq operator returns all elements that are equal to your term, the -like operator returns all elements that match the wildcard given in your pattern, and the -match operator returns all elements that match the regular expression given in your pattern.

For more complex comparison conditions, the Where-Object cmdlet lets you find elements in a list that satisfy much more complex conditions:

PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12"
PS > $array | Where-Object { $_.Length -gt 6 }
Item 12

For more information, see Filter Items in a List or Command Output.

For more information about the -eq, -like, and -match operators, see Comparison Operators.

Compare Two Lists

Problem

You have two lists and want to find items that exist in only one or the other list.

Solution

To compare two lists, use the Compare-Object cmdlet:

PS > $array1 = "Item 1","Item 2","Item 3","Item 1","Item 12"
PS > $array2 = "Item 1","Item 8","Item 3","Item 9","Item 12"
PS > Compare-Object $array1 $array2

InputObject                           SideIndicator
-----------                           -------------
Item 8                                =>
Item 9                                =>
Item 2                                <=
Item 1                                <=

Discussion

The Compare-Object cmdlet lets you compare two lists. By default, it shows only the items that exist exclusively in one of the lists, although its -IncludeEqual parameter lets you include items that exist in both. If it returns no results, the two lists are equal.

For more information, see Chapter 22.

Remove Elements from an Array

Problem

You want to remove all elements from an array that match a given item or term—either exactly, by pattern, or by regular expression.

Solution

To remove all elements from an array that match a pattern, use the -ne, -notlike, and -notmatch comparison operators, as shown in Example 7-2.

Example 7-2. Removing elements from an array using the -ne, -notlike, and -notmatch operators

PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12"
PS > $array -ne "Item 1"
Item 2
Item 3
Item 12
PS > $array -notlike "*1*"
Item 2
Item 3
PS > $array -notmatch "Item .."
Item 1
Item 2
Item 3
Item 1

To actually remove the items from the array, store the results back in the array:

PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12"
PS > $array = $array -ne "Item 1"
PS > $array
Item 2
Item 3
Item 12

Discussion

The -eq, -like, and -match operators are useful ways to find elements in a collection that match your given term. Their opposites, the -ne, -notlike, and -notmatch operators, return all elements that do not match that given term.

To remove all elements from an array that match a given pattern, you can then save all elements that do not match that pattern.

For more information about the -ne, -notlike, and -notmatch operators, see Comparison Operators.

Find Items in an Array Greater or Less Than a Value

Problem

You have an array and want to find all elements greater or less than a given item or value.

Solution

To find all elements greater or less than a given value, use the -gt, -ge, -lt, and -le comparison operators:

PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12"
PS > $array -ge "Item 3"
Item 3
PS > $array -lt "Item 3"
Item 1
Item 2
Item 1
Item 12

Discussion

The -gt, -ge, -lt, and -le operators are useful ways to find elements in a collection that are greater or less than a given value. Like all other PowerShell comparison operators, these use the comparison rules of the items in the collection. Since the array in the solution is an array of strings, this result can easily surprise you:

PS > $array -lt "Item 2"
Item 1
Item 1
Item 12

The reason for this becomes clear when you look at the sorted array—"Item 12" comes before "Item 2" alphabetically, which is the way that PowerShell compares arrays of strings.

PS > $array | Sort-Object
Item 1
Item 1
Item 12
Item 2
Item 3

For more information about the -gt, -ge, -lt, and -le operators, see Comparison Operators.

Use the ArrayList Class for Advanced Array Tasks

Problem

You have an array that you want to frequently add elements to, remove elements from, search, and modify.

Solution

To work with an array frequently after you define it, use the System.Collections.ArrayList class:

PS > $myArray = New-Object System.Collections.ArrayList
PS > [void] $myArray.Add("Hello")
PS > [void] $myArray.AddRange( ("World","How","Are","You") )
PS > $myArray
Hello
World
How
Are
You
PS > $myArray.RemoveAt(1)
PS > $myArray
Hello
How
Are
You

Discussion

Like in most other languages, arrays in PowerShell stay the same length once you create them. PowerShell allows you to add items, remove items, and search for items in an array, but these operations may be time-consuming when you are dealing with large amounts of data. For example, to combine two arrays, PowerShell creates a new array large enough to hold the contents of both arrays and then copies both arrays into the destination array.

In comparison, the ArrayList class is designed to let you easily add, remove, and search for items in a collection.

Note

PowerShell passes along any data that your script generates, unless you capture it or cast it to [void]. Since it is designed primarily to be used from programming languages, the System.Collections.ArrayList class produces output, even though you may not expect it to. To prevent it from sending data to the output pipeline, either capture the data or cast it to [void]:

PS > $collection = New-Object System.Collections.ArrayList
PS > $collection.Add("Hello")
0
PS > [void] $collection.Add("World")

If you plan to add and remove data to and from an array frequently, the System.Collections.ArrayList class provides a more dynamic alternative.

For more information about working with classes from the .NET Framework, see Work with .NET Objects.

Create a Hashtable or Associative Array

Problem

You have a collection of items that you want to access through a label that you provide.

Solution

To define a mapping between labels and items, use a hashtable (associative array):

PS > $myHashtable = @{ Key1 = "Value1"; "Key 2" = 1,2,3 }
PS > $myHashtable["New Item"] = 5
PS >
PS > $myHashTable

Name                           Value
----                           -----
Key 2                          {1, 2, 3}
New Item                       5
Key1                           Value1

Discussion

Hashtables are much like arrays that let you access items by whatever label you want—not just through their index in the array. Because of that freedom, they form the keystone of a huge number of scripting techniques. Since they let you map names to values, they form the natural basis for lookup tables such as those for zip codes and area codes. Since they let you map names to fully featured objects and script blocks, they can often take the place of custom objects. And since you can map rich objects to other rich objects, they can even form the basis of more advanced data structures such as caches and object graphs.

The solution demonstrates how to create and initialize a hashtable at the same time, but you can also create one and work with it incrementally:

PS > $myHashtable = @{}
PS > $myHashtable["Hello"] = "World"
PS > $myHashtable.AnotherHello = "AnotherWorld"
PS > $myHashtable

Name                           Value
----                           -----
AnotherHello                   AnotherWorld
Hello                          World

This ability to map labels to structured values also proves helpful in interacting with cmdlets that support advanced configuration parameters, such as the calculated property parameters available on the Format-Table and Select-Object cmdlets. For an example of this use, see Display the Properties of an Item as a Table.

For more information about working with hashtables, see Hashtables (Associative Arrays).

Sort a Hashtable by Key or Value

Problem

You have a hashtable of keys and values, and you want to get the list of values that result from sorting the keys in order.

Solution

To sort a hashtable, use the GetEnumerator() method on the hashtable to gain access to its individual elements. Then, use the Sort-Object cmdlet to sort by Name or Value.

foreach($item in $myHashtable.GetEnumerator() | Sort Name)
{
    $item.Value
}

Discussion

Since the primary focus of a hashtable is to simply map keys to values, you should not depend on it to retain any ordering whatsoever—such as the order you added the items, the sorted order of the keys, or the sorted order of the values.

This becomes clear in Example 7-3.

Example 7-3. A demonstration of hashtable items not retaining their order

PS > $myHashtable = @{}
PS > $myHashtable["Hello"] = 3
PS > $myHashtable["Ali"] = 2
PS > $myHashtable["Alien"] = 4
PS > $myHashtable["Duck"] = 1
PS > $myHashtable["Hectic"] = 11
PS > $myHashtable

Name                           Value
----                           -----
Hectic                         11
Duck                           1
Alien                          4
Hello                          3
Ali                            2

However, the hashtable object supports a GetEnumerator() method that lets you deal with the individual hashtable entries—all of which have a Name and Value property. Once you have those, we can sort by them as easily as we can sort any other PowerShell data. Example 7-4 demonstrates this technique.

Example 7-4. Sorting a hashtable by name and value

PS > $myHashtable.GetEnumerator() | Sort Name

Name                           Value
----                           -----
Ali                            2
Alien                          4
Duck                           1
Hectic                         11
Hello                          3

PS > $myHashtable.GetEnumerator() | Sort Value

Name                           Value
----                           -----
Duck                           1
Ali                            2
Hello                          3
Alien                          4
Hectic                         11

For more information about working with hashtables, see Hashtables (Associative Arrays).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.144.108