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 solves that need perfectly.
I would mention that PowerShell distinguishes between arrays and Hashtables. In order to sort a Hashtable by its key, a user would need to call GetEnumerator() before piping it into the sort-object cmdlet for example. An array doesn't need that extra call. This is a common mistake.
Another note at this point could be quite helpful to a reader. PowerShell treats an array with one item like the contained object and not like an array with one item. This is also confusing and causes lots of headaches for beginners. A recipe could be added that explains how to deal with this edge case of an collection only having one item.
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 "Syste
m.Int32": "Cannot convert value "Hello" to type "System.Int32". Error: "In
put 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, “Using an ArrayList to manage a dynamic collection of
items”.
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
YouAside 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.
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 the section called “Creating Instances of Types”.
For more information on lists and arrays in PowerShell, see the section called “Arrays and Lists”.
You want to create an array of arrays, or an array of multiple dimensions.
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
Jagged and multidimensional arrays are useful for holding lists of lists/arrays of arrays. Jagged arrays are arrays of arrays, where each array only needs to have as many elements as it needs. A non-jagged 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), while nonjagged multidimensional arrays are sometimes useful for dealing with large grids of data.
I don't understand what you mean by "Jagged"...
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 the section called “Arrays and Lists”.
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
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.
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 the section called “Create a Hashtable or Associative Array”.
For more information on lists and arrays in PowerShell (including the array ranges and slicing syntax), see the section called “Arrays and Lists”.
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
6It should be emphesized, that the pipe system "flattens" arrays: PS C:> (1,(2,3) | %{$_}).count 3 So the output of 1,(2,3) | %{$_} has not two elements: 1 and (2,3), but has a three elements: 1 and 2 and 3.
But: PS C:> (1,(2,3),(4,(5,6)) | %{$_})[4] 5 6 So this "flattening" is only 1 level deep.
To access each item in an array in a more
scriptlike fashion, use the foreach
scripting keyword:
PS > $myArray = 1,2,3
PS > $sum = 0
PS > foreach($element in $myArray) { $sum += $element }
PS > $sum
6To 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
6PowerShell 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, while 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 the section called “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
the section called “Repeat Operations with Loops”.
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
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. In this example, sorting by the second character:
PS > "Hello","World","And","PowerShell" | Sort-Object { $_.Substring(1,1) }
Hello
And
PowerShell
WorldMaybe a good idea to mention that is possible to sort an array and assign it back to varialble:
$array = "Hello","World","And","PowerShell" $array = $array | Sort-Object
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.
“cmdlet's –Unique switch” → “cmdlet's -Unique switch” (en dash → hyphen-minus)
Apologies if that's overzealous with those dashes/hyphens. In practice probably no-one would notice if it's the one or the other. However, in many cases there are both en dashes in front of parameter or operator names as well as hyphens in cmdlet names or also in operator/parameter names right beside the other use. In those cases it is visible.
I'd aim for consistency here, regardless of which character you choose, but for electronic variants of the book a hyphen is much easier to search for, too. People will probably search for “-eq” instead of “–eq”.
I'm not terribly sure whether to continue looking for those things, though. It might distract from content issues which then drown in a flood of tiny typographic nitpicks.
You want to determine whether an array or list contains a specific item.
To determine whether a list contains a
specific item, use the –contains
operator:
“use the –contains operator” → “use the -contains operator” (en dash → hyphen-minus)
PS > "Hello","World" -contains "Hello" True PS > "Hello","World" -contains "There" False
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.
–contains” → “-contains”
–match” → “-match”
–like” → “-like”
(en dash → hyphen-minus)
For more information about the –contains, -match, and –like operators, see the section called “Comparison Operators”.
–contains” → “-contains”
–like” → “-like”
(en dash → hyphen-minus)
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
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 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 the section called “Use the ArrayList Class for Advanced Array Tasks”.
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.
To find all elements that match an item, use
the –eq, -like, and –match comparison operators:
–eq” → “-eq”
–match” → “-match”
(en dash → hyphen-minus)
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
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.
–eq” → “-eq”
–like” → “-like”
–match” → “-match”
(en dash → hyphen-minus)
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 12For more information, see the section called “Filter Items in a List or Command Output”.
For more information about the –eq, -like,
and –match operators, see the section called “Comparison Operators”.
–eq” → “-eq”
–match” → “-match”
(en dash → hyphen-minus)
You have two lists and want to find items that exist in only one or the other of them.
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 <=
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, Comparing Data.
You want to remove all elements from an array that match a given item or term—either exactly, by pattern, or by regular expression.
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, “Removing elements from an array using the–ne, -notlike,
and–notmatch operators”.
–ne” → “-ne”
–notmatch” → “-notmatch”
(en dash → hyphen-minus)
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
In the example title:
“using the–ne, -notlike, and–notmatch operators” → “using the -ne, -notlike, and -notmatch operators” (en dash → hyphen-minus, missing spaces before -ne and -notmatch)
I guess, code formatting for the operators in the example title is impossible due to other formatting constraints, right?
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
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.
“the –ne, -notlike, and –notmatch operators” → “the -ne, -notlike, and -notmatch operators” (en dashes → hyphen-minus)
To remove all elements from an array that match a given pattern, then, you can save all elements that do not match that pattern.
For more information about the –ne, -notlike, and –notmatch operators, see the section called “Comparison Operators”.
“the –ne, -notlike, and –notmatch operators” → “the -ne, -notlike, and -notmatch operators” (en dashes → hyphen-minus)
You have an array and want to find all elements greater or less than a given item or value.
To find all elements greater or less than a
given value, use the –gt, –ge, -lt,
and –le comparison operators:
“the –gt, –ge, -lt, and –le comparison operators” → “the -gt, -ge, -lt, and -le comparison operators” (en dashes → hyphen-minus)
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
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 the section called “Comparison Operators”.
You have an array that you want to frequently add elements to, remove elements from, search, and modify.
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
YouLike 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.
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")Or cmdlet way:
PS >$collection.Add("There") | Out-Null
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 the section called “Work with .NET Objects”.
You have a collection of items that you want to access through a label that you provide.
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 Value1From example it looks that it's necessary to initialize hash. Maybe it's worth mentioning that it's possible to start directly with assigning at line three.
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 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. 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 WorldThis 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 the section called “Display the Properties of an Item As a Table”.
For more information about working with hashtables, see the section called “Hashtables (Associative Arrays)”.
You have a hashtable of keys and values, and want to get the list of values that result from sorting the keys in order.
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
}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, “A demonstration of hashtable items not retaining their order”.
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 2However, 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, “Sorting a hashtable by name and value” 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 the section called “Hashtables (Associative Arrays)”.
Add a recipe for determining that two collections contain the same items and the order doesn't matter. Add a recipe for determining that two collections are identical. Same items and same order. Add a recipe for asserting that one collection is a subset of the other collection.
One other cool recipe would be about how to create generic collections like for example a list
I'd add a recipe for exporting a hashtable
Thanks Klaus, I added those recipes. Karl: This is covered in the generic "Easily Export Your Structured Data" recipe. Is there a reason you think it should be mentioned explicitly for hashtables?
No comments yet
Add a comment