您想知道有關於陣列的一切

陣列是大部分程式設計語言的基礎語言功能。 陣列是不容易規避的值或物件的集合。 讓我們詳細檢視陣列及其所提供的一切功能。

注意

本文 的原始版本 會出現在 由 @KevinMarquette撰寫的部落格上。 PowerShell 小組感謝 Kevin 與我們分享此內容。 請前往 PowerShellExplained.com 瀏覽他的部落格。

什麼是陣列?

我要先從基本的技術描述開始,藉此說明陣列是什麼,以及大部分的程式設計語言如何使用這些陣列,再導入 PowerShell 使用陣列的其他方式的主題。

陣列是一種資料結構,可做為多個項目的集合。 您可以逐一查看陣列,或使用索引來存取個別的項目。 系統會以連續的記憶體區塊建立陣列,其中每個值都會儲存於彼此相鄰的位置。

隨著文章內容的敘述,我將處及相關的詳細資訊。

基本使用方式

因為陣列是 PowerShell 的基本功能,所以在 PowerShell 中使用陣列有一種簡單的語法。

建立陣列

您可以使用 @() 來建立空的陣列

PS> $data = @()
PS> $data.count
0

我們可以建立陣列,並將值放在 @() 括弧中,藉此將其植入。

PS> $data = @('Zero','One','Two','Three')
PS> $data.count
4

PS> $data
Zero
One
Two
Three

此陣列有 4 個項目。 當我們呼叫 $data 變數時,我們會看到項目的清單。 如果是字串陣列,則會為每個字串各取得一行。

我們可以在多行宣告陣列。 在此情況下,逗號是選擇性使用的,而且通常會省略。

$data = @(
    'Zero'
    'One'
    'Two'
    'Three'
)

我習慣將陣列宣告為多行,就像這樣。 當您有多個項目時,這樣不僅方便閱讀,同時也可讓您在使用原始程式碼控制時更容易與舊版進行比較。

其他語法

眾所周知,@() 是用來建立陣列的語法,不過在大多數情況下,以逗號分隔的清單就能勝任。

$data = 'Zero','One','Two','Three'

Write-Output 建立陣列

值得一提的小技巧,就是您可以使用 Write-Output 在主控台快速地建立字串。

$data = Write-Output Zero One Two Three

這種方式很便利,因為當參數接受字串時,您不需要在字串前後加上引號。 我不會在指令碼中執行這項操作,不過這種作法在主控台中是很好下手的方式。

存取項目

現在您已經有一個具有項目的陣列,您可能會想要存取和更新這些項目。

Offset

若要存取個別的項目,我們將括弧 [] 用於從 0 開始的位移值。 這就是我們取得陣列中第一個項目的方式:

PS> $data = 'Zero','One','Two','Three'
PS> $data[0]
Zero

我們在這裡使用零的原因,在於第一個項目位於清單的開頭,所以我們使用 0 個項目的位移來取得該項目。 若要取得第二個項目,我們必須藉由位移 1 來略過第一個項目。

PS> $data[1]
One

這表示最後一個項目是在位移 3。

PS> $data[3]
Three

索引

現在您可以瞭解我在此範例中挑選那些值的原因所在。 我將此視為位移,因為這就是它真正的本質所在,但此位移較常稱為索引。 從 0 開始的索引。 在本文的其餘部分,我將會將該位移稱為索引。

特殊索引訣竅

在大部分的程式設計語言中,您只能指定單一數字做為索引,並傳回單一項目。 PowerShell 更加靈活。 您可以同時使用多個索引。 藉由提供索引清單,我們可以選取多個項目。

PS> $data[0,2,3]
Zero
Two
Three

系統會根據所提供的索引順序傳回項目。 如果複製索引,則您會同時取得該項目。

PS> $data[3,0,3]
Three
Zero
Three

我們可以藉由內建的 .. 運算子來指定數字序列。

PS> $data[1..3]
One
Two
Three

反向也適用。

PS> $data[3..1]
Three
Two
One

您可以使用負索引值從尾端進行位移。 因此,如果需要清單中的最後一個項目,您可以使用 -1

PS> $data[-1]
Three

在這裡使用 .. 運算子時,請務必小心謹慎。 序列 0..-1-1..0 會針對 0,-1-1,0 的值進行評估。 如果您忘記此詳細資料,很容易就能看到 $data[0..-1],並聯想到它會列舉所有的項目。 $data[0..-1] 藉由提供陣列中的第一個和最後一個項目 (並且不含任何其他值),為您提供與 $data[0,-1] 相同的值。 以下是較大的範例:

PS> $a = 1,2,3,4,5,6,7,8
PS> $a[2..-1]
3
2
1
8

這與:

PS> $a[2,1,0,-1]
3
2
1
8

超出範圍

在大部分的程式設計語言中,如果嘗試存取超出陣列結尾的項目索引,則您會收到某種類型的錯誤或例外狀況。 PowerShell 不會無訊息地傳回任何內容。

PS> $null -eq $data[9000]
True

無法為 null 陣列編製索引

如果您的變數是 $null,而您嘗試將其編製為數組之類的索引,則會收到顯示訊息 Cannot index into a null arraySystem.Management.Automation.RuntimeException 例外狀況。

PS> $empty = $null
PS> $empty[0]
Error: Cannot index into a null array.

因此,在您嘗試存取其中的元素之前,請確認您的陣列並非 $null

Count

陣列和其他集合具有 count 屬性,可告訴您陣列中有多少個項目。

PS> $data.count
4

PowerShell 3.0 已將 count 屬性新增至大部分的物件。 您可以擁有單一物件,而且物件應該會提供您 1 的計數。

PS> $date = Get-Date
PS> $date.count
1

即使 $null 具有 count 屬性,它還是會傳回 0

PS> $null.count
0

這裡有一些陷阱,我會在本文稍後探討 $null 或空白陣列的檢查時,再回顧一下。

差一錯誤

會建立常見的程式設計錯誤的原因,在於陣列是從索引 0 開始。 有兩種方式可以導入差一錯誤。

第一種是心裡想著您想要第二個項目,並使用 2 的索引,然後實際上去取得第三個項目。 或者,假設您有四個項目,而您想要上一個項目,所以您可以使用此計數來取用最後一個項目。

$data[ $data.count ]

PowerShell 很樂意讓您這麼做,並為您提供索引 4的確切項目: $null。 您應該使用 $data.count - 1 或我們所學習的 -1

PS> $data[ $data.count - 1 ]
Three

這是您可以使用 -1 索引來取得最後一項元素的最後機會。

PS> $data[ -1 ]
Three

Lee Dailey 也會向我指出,我們可以使用 $data.GetUpperBound(0) 來取得最大的索引編號。

PS> $data.GetUpperBound(0)
3
PS> $data[ $data.GetUpperBound(0) ]
Three

第二個最常見的方式是在反覆查看清單,而不是在合適的時間停止。 當我們談到如何使用 for 迴圈時,我會重新回顧此資訊。

正在更新項目

我們可以使用相同的索引來更新陣列中的現有項目。 這可讓我們直接存取以更新個別項目。

$data[2] = 'dos'
$data[3] = 'tres'

如果我們嘗試更新最後一項元素之外的項目,就會出現 Index was outside the bounds of the array. 錯誤。

PS> $data[4] = 'four'
Index was outside the bounds of the array.
At line:1 char:1
+ $data[4] = 'four'
+ ~~~~~~~~~~~~~
+ CategoryInfo          : OperationStopped: (:) [], IndexOutOfRangeException
+ FullyQualifiedErrorId : System.IndexOutOfRangeException

稍後當我談到如何擴大陣列時,我會重新回顧這種情況。

反覆運算

在某些時候,您可能需要針對陣列中的每個項目,逐一執行或逐一查看整個清單並執行動作。

管線

陣列和 PowerShell 管道皆彼此適用。 這是處理這些值最簡單的方式之一。 當您將陣列傳遞至管道時,陣列中的每個項目都會進行個別處理。

PS> $data = 'Zero','One','Two','Three'
PS> $data | ForEach-Object {"Item: [$PSItem]"}
Item: [Zero]
Item: [One]
Item: [Two]
Item: [Three]

如果您之前看不到 $PSItem,只需要知道它與 $_ 的內容相同即可。 您可以使用其中一種,因為兩者都代表管道中的目前物件。

ForEach 迴圈

ForEach 迴圈適用於集合。 使用語法: foreach ( <variable> in <collection> )

foreach ( $node in $data )
{
    "Item: [$node]"
}

ForEach 方法

我往往會忘了這項功能,但它很適合用於簡單的作業。 PowerShell 可讓您在集合上呼叫 .ForEach()

PS> $data.foreach({"Item [$PSItem]"})
Item [Zero]
Item [One]
Item [Two]
Item [Three]

.foreach() 接受屬於指令碼區塊的參數。 您可以置放括號,並僅提供指令碼區塊。

$data.foreach{"Item [$PSItem]"}

這是鮮為人知的語法,但其運作方式完全相同。 此 foreach 方法已在 PowerShell 4.0 中新增。

For 迴圈

在大部分其他程式設計語言中,皆使用 for 迴圈,不過在 PowerShell 中並不多見。 當您看到該迴圈時,通常是在逐一執行陣列的內容中。

for ( $index = 0; $index -lt $data.count; $index++)
{
    "Item: [{0}]" -f $data[$index]
}

我們首先該做的,就是將 $index 初始化為 0。 然後,我們會新增 $index 必須小於 $data.count 的條件。 最後,我們會指定每次迴圈時,我都必須藉由 1 來增加索引。 在此情況下,$index++$index = $index + 1 縮短形。 格式運算子 (-f) 用來在輸出字串中插入 的值 $data[$index]

當您使用 for 迴圈時,請特別注意該條件。 我在這裡使用 $index -lt $data.count。 您可以稍微讓條件出現錯誤,藉此在您的邏輯中取得差一錯誤。 使用 $index -le $data.count$index -lt ($data.count - 1) 總是略有些許錯誤。 這會導致您的結果處理的項目過多或太少。 這是傳統的差一錯誤。

Switch 迴圈

這是很容易忽略的一種。 如果您將陣列提供給 switch 陳述式,則陳述式會檢查陣列中的每個項目。

$data = 'Zero','One','Two','Three'
switch( $data )
{
    'One'
    {
        'Tock'
    }
    'Three'
    {
        'Tock'
    }
    Default
    {
        'Tick'
    }
}
Tick
Tock
Tick
Tock

我們可以使用 switch 陳述式來做很多很酷的事。 我有另一篇專文介紹這個。

更新值

當陣列是字串或整數 (實值型別) 的集合時,您有時可能會在進行迴圈處理時,想要新陣列中的值。 以上大部分的迴圈會使用迴圈中的變數來保存值的複本。 如果您更新該變數,陣列中的原始值不會更新。

該陳述式的例外狀況是 for 迴圈。 如果您想要逐一執行陣列並更新其中的值,則 for 迴圈就是您要尋找的工具。

for ( $index = 0; $index -lt $data.count; $index++ )
{
    $data[$index] = "Item: [{0}]" -f $data[$index]
}

此範例會依索引採用值,進行一些變更,然後使用該相同的索引將其指派回去。

物件陣列

到目前為止,我們放入陣列中的唯一東西就是實值型別,不過陣列也可以包含物件。

$data = @(
    [pscustomobject]@{FirstName='Kevin';LastName='Marquette'}
    [pscustomobject]@{FirstName='John'; LastName='Doe'}
)

當您將物件的集合指派給變數時,許多 Cmdlet 都會以陣列的形式將其傳回。

$processList = Get-Process

我們已討論過的所有基本功能,仍適用於物件陣列,其中有一些值得加以說明的細節。

存取屬性

我們可以使用索引來存取集合中的個別項目,就像使用實值型別一樣。

PS> $data[0]

FirstName LastName
-----     ----
Kevin     Marquette

我們可以直接存取和更新屬性。

PS> $data[0].FirstName

Kevin

PS> $data[0].FirstName = 'Jay'
PS> $data[0]

FirstName LastName
-----     ----
Jay       Marquette

陣列屬性

通常您必須列舉這類的完整清單,才能存取所有屬性:

PS> $data | ForEach-Object {$_.LastName}

Marquette
Doe

或藉由使用 Select-Object -ExpandProperty Cmdlet 達到目的。

PS> $data | Select-Object -ExpandProperty LastName

Marquette
Doe

不過,PowerShell 讓我們能夠直接要求 LastName。 PowerShell 會為我們列舉所有清單,並傳回一份清除清單。

PS> $data.LastName

Marquette
Doe

列舉仍然會發生,但我們無法瞭解其背後的複雜性。

Where-Object 篩選

這就是 Where-Object 的進入位置,因此我們可以根據物件的屬性,篩選並選取我們想要的陣列內容。

PS> $data | Where-Object {$_.FirstName -eq 'Kevin'}

FirstName LastName
-----     ----
Kevin     Marquette

我們可以撰寫相同的查詢,以取得我們所要尋找的 FirstName

$data | Where FirstName -eq Kevin

Where()

陣列具有 Where() 的方法,可讓您指定篩選條件的 scriptblock

$data.Where({$_.FirstName -eq 'Kevin'})

此功能已在 PowerShell 4.0 中新增。

更新迴圈中的物件

採用實值型別時,更新陣列的唯一方式是使用 for 迴圈,因為我們需要知道索引以取代值。 我們有更多選項可使用物件,因為它們是參考型別。 以下是一個簡短的範例:

foreach($person in $data)
{
    $person.FirstName = 'Kevin'
}

這個迴圈會逐一執行 $data 陣列中的每個物件。 由於物件屬於參考型別,因此 $person 變數會參考陣列中完全相同的物件。 因此,更新其屬性會更新原始物件。

您仍然無法以這種方式來取代整個物件。 如果您嘗試將新物件指派給 $person 變數,則會將變數參考更新為不會再指向陣列中原始物件的其他物件。 這種運作方式與您的預期不同:

foreach($person in $data)
{
    $person = [pscustomobject]@{
        FirstName='Kevin'
        LastName='Marquette'
    }
}

操作員

PowerShell 中的運算子也適用陣列。 其中一些工作的執行方式稍有不同。

-join

-join 運算子最明顯,因此讓我們先瞭解一下。 我喜歡 -join 運算子並經常使用。 它會將陣列中的所有元素與您指定的字元或字串聯結在一起。

PS> $data = @(1,2,3,4)
PS> $data -join '-'
1-2-3-4
PS> $data -join ','
1,2,3,4

我喜歡 -join 運算子的其中一項功能,就是它會處理單一項目。

PS> 1 -join '-'
1

我在記錄和詳細資訊訊息中使用此功能。

PS> $data = @(1,2,3,4)
PS> "Data is $($data -join ',')."
Data is 1,2,3,4.

-join $array

Lee Dailey 在這裡向我傳授一個聰明的技巧。 如果您想要加入任何不含分隔符號的內容,您可以改用以下方式:

PS> $data = @(1,2,3,4)
PS> $data -join $null
1234

您可以使用 -join 搭配陣列做為參數,而不使用前置詞。 請檢視此範例,瞭解我所說的內容。

PS> $data = @(1,2,3,4)
PS> -join $data
1234

-replace 與 -split

-replace-split 等其他運算子會在陣列中的每個項目上執行。 我不能說我曾用過這種方式,但這裡有一個範例。

PS> $data = @('ATX-SQL-01','ATX-SQL-02','ATX-SQL-03')
PS> $data -replace 'ATX','LAX'
LAX-SQL-01
LAX-SQL-02
LAX-SQL-03

-contains

-contains 運算子可讓您檢查值的陣列,以查看其是否包含指定的值。

PS> $data = @('red','green','blue')
PS> $data -contains 'green'
True

-in

當您有想要驗證的單一值符合數個值的其中之一時,您可以使用 -in 運算子。 這個值會在左邊,而陣列位於運算子右側。

PS> $data = @('red','green','blue')
PS> 'green' -in $data
True

如果清單很大,這可能會產生昂貴的費用。 如果我檢查的值不是少數幾個,我通常會使用 RegEx 模式。

PS> $data = @('red','green','blue')
PS> $pattern = "^({0})$" -f ($data -join '|')
PS> $pattern
^(red|green|blue)$

PS> 'green' -match $pattern
True

-eq 和-ne

等式和陣列可能會變得複雜。 當陣列位於左側時,會比較每個項目。 這樣並不會傳回 True,而是會傳回符合的物件。

PS> $data = @('red','green','blue')
PS> $data -eq 'green'
green

當您使用 -ne 運算子時,我們會取得所有與我們值不相等的值。

PS> $data = @('red','green','blue')
PS> $data -ne 'green'
red
blue

當您在 if() 陳述式中使用這種方式時,傳回的值就是 True 值。 如果未傳回任何值,則為 False 值。 這兩個後續陳述式都會評估為 True

$data = @('red','green','blue')
if ( $data -eq 'green' )
{
    'Green was found'
}
if ( $data -ne 'green' )
{
    'And green was not found'
}

我稍後會回顧一下我們談到的 $null 的測試。

-match

-match 運算子會嘗試比對集合中的每個項目。

PS> $servers = @(
    'LAX-SQL-01'
    'LAX-API-01'
    'ATX-SQL-01'
    'ATX-API-01'
)
PS> $servers -match 'SQL'
LAX-SQL-01
ATX-SQL-01

當您使用具有單一值的 -match 時,會以符合的資訊填入一個特殊的變數 $Matches。 若以這種方式處理陣列,就不會出現這種情況。

我們可以採用與 Select-String 相同的方法。

$servers | Select-String SQL

我會進一步檢視 Select-String-match 和另一篇文章 (使用 RegEx 的許多方式 (英文)) 中的 $matches 變數。

$null 或 empty

測試 $null 或空陣列可能會很棘手。 以下是陣列的常見陷阱。

乍看之下,這個陳述式看起來應該可以運作。

if ( $array -eq $null)
{
    'Array is $null'
}

但是我只是在 -eq 檢查陣列中的每個項目。 因此,我們可以擁有數個項目的陣列,其中包含單一 $null 值,而且該值會評估為 $true

$array = @('one',$null,'three')
if ( $array -eq $null)
{
    'I think Array is $null, but I would be wrong'
}

這就是將 $null 放在運算子左邊是最佳做法的理由所在。 這會讓此情況成為不會發生的問題。

if ( $null -eq $array )
{
    'Array actually is $null'
}

$null 陣列與空陣列屬於不同的陣列。 若您知道自己有陣列,請檢查其中的物件計數。 如果陣列為 $null,則計數為 0

if ( $array.count -gt 0 )
{
    "Array isn't empty"
}

這裡還有另一個要注意的陷阱。 即使您有單一物件,您仍然可以使用 count,除非該物件是 PSCustomObject。 這是 PowerShell 6.1 中所修正的錯誤。 這是好消息,但有很多人仍在使用 5.1,而且需要留意。

PS> $object = [PSCustomObject]@{Name='TestObject'}
PS> $object.count
$null

如果您仍在使用 PowerShell 5.1,您可以先將物件包裝在陣列中,再檢查計數以取得精確的計數。

if ( @($array).count -gt 0 )
{
    "Array isn't empty"
}

為了充分確保安全起見,請檢查 $null,然後檢查計數。

if ( $null -ne $array -and @($array).count -gt 0 )
{
    "Array isn't empty"
}

全部 -eq

我最近看到有人詢問 如何驗證陣列中的每個值是否符合指定的值。 Reddit 使用者 /u/bis 享有這項聰明的 解決方案,可檢查是否有任何不正確的值,然後將結果翻轉。

$results = Test-Something
if ( -not ( $results -ne 'Passed') )
{
    'All results a Passed'
}

新增至陣列

此時,您會開始對如何將項目新增至陣列感到懷疑。 簡單明瞭的答案就是您無法將項目新增至陣列。 陣列在記憶體中的大小是固定的。 如果您需要擴充陣列或在其中加入單一項目,則需要建立新的陣列,並將所有的值複製到舊陣列。 就跟很多工作一樣,這聽起來十分費工;不過,PowerShell 會隱藏建立新陣列的複雜性。 PowerShell 會為陣列實作新增運算子 (+)。

注意

PowerShell 不會實作減法運算。 如果您想要有彈性的陣列替代方案,則必須使用泛型List物件。

新增陣列

我們可以搭配使用新增運算子與陣列來建立新的陣列。 因此,假設有這兩個陣列:

$first = @(
    'Zero'
    'One'
)
$second = @(
    'Two'
    'Three'
)

我們可以將其合併以取得新的陣列。

PS> $first + $second

Zero
One
Two
Three

Plus equals +=

我們可以就地建立新的陣列,並在其中新增項目,如下所示:

$data = @(
    'Zero'
    'One'
    'Two'
    'Three'
)
$data += 'four'

請記住,您每次使用 += 時,都會複製並建立新的陣列。 這不是小型資料集的問題,而是以極不理想的方式進行規模調整。

管道指派

您可以將任何管道的結果指派至變數中。 如果其中包含多個項目,則為陣列。

$array = 1..5 | ForEach-Object {
    "ATX-SQL-$PSItem"
}

通常當我們想要使用管道時,我們會考慮使用一般的 PowerShell 一行程式碼。 我們可以利用管道搭配 foreach() 陳述式和其他迴圈。 因此,我們不會將項目新增至迴圈中的陣列,而是可以將項目放置在管道上。

$array = foreach ( $node in (1..5))
{
    "ATX-SQL-$node"
}

陣列類型

根據預設,系統建立 PowerShell 中的陣列做為 [PSObject[]] 類型。 如此可使其包含任何類型的物件或值。 這種方式有效,是因為所有項目都是從 PSObject 類型繼承而來。

強類型陣列

您可以使用類似的語法來建立任何類型的陣列。 當您建立強類型陣列時,它只能包含指定類型的值或物件。

PS> [int[]] $numbers = 1,2,3
PS> [int[]] $numbers2 = 'one','two','three'
ERROR: Cannot convert value "one" to type "System.Int32". Input string was not in a correct format."

PS> [string[]] $strings = 'one','two','three'

ArrayList

將項目新增至陣列是其最大的限制之一,但還有一些其他集合可供我們使用,從而解決這個問題。

當我們需要更快速使用的陣列時,ArrayList 通常是我們想到的第一個辦法。 它的作用就像是物件陣列,我們在任何場合都需要它,但它會快速地處理新增項目。

以下是我們建立 ArrayList 並在其中新增項目的方式。

$myarray = [System.Collections.ArrayList]::new()
[void]$myArray.Add('Value')

我們會呼叫 .NET 來取得這種類型。 在此情況下,我們會使用預設的建構函式來建立它。 然後,我們會呼叫 Add 方法,並將項目新增至其中。

我在行首使用 [void] 的原因,是要隱藏傳回程式碼。 有些 .NET 呼叫會執行此動作,而且可能會產生非預期的輸出。

如果您在陣列中的唯一資料是字串,則也請瞭解 StringBuilder 的使用方式。 這幾乎是相同的東西,但有一些僅供處理字串的方法。 StringBuilder 是專為效能而特別設計的。

人們從陣列移至 ArrayList 的情況很常見。 但這種情況發生在 C# 並未提供泛型支援的時代。 ArrayList 在泛型的 List[] 支援中已淘汰

泛型清單

泛型型別是中的特殊類型 C# ,可定義一般化類別,而使用者會指定它在建立時所使用的資料類型。 因此,如果您需要數字或字串的清單,您可以定義想要 intstring 類型的清單。

以下是建立字串清單的方式。

$mylist = [System.Collections.Generic.List[string]]::new()

或者您也可以數字的清單。

$mylist = [System.Collections.Generic.List[int]]::new()

我們可以將現有的陣列轉換成類似如下的清單,而且不需要先建立物件:

$mylist = [System.Collections.Generic.List[int]]@(1,2,3)

我們可以使用 PowerShell 5 和更新版本中的 using namespace 陳述式來縮短語法。 using 陳述式必須是指令碼的第一行。 藉由宣告命名空間,PowerShell 可讓您在參考資料類型時,將其剔除。

using namespace System.Collections.Generic
$myList = [List[int]]@(1,2,3)

這可讓 List 變得更容易使用。

您可以使用類似 Add 的方法。 有別於 ArrayList,Add 方法沒有傳回值,因此我們不需要 void 它。

$myList.Add(10)

我們仍然可以存取其他陣列之類的元素。

PS> $myList[-1]
10

List[PSObject]

您可擁有任何類型的清單,但在不知道物件類型時,可使用 [List[PSObject]] 加以包含。

$list = [List[PSObject]]::new()

Remove()

ArrayList 和泛型 List[] 都支援從集合中移除項目。

using namespace System.Collections.Generic
$myList = [List[string]]@('Zero','One','Two','Three')
[void]$myList.Remove("Two")
Zero
One
Three

使用實值型別時,它會從清單中移除第一個項目。 您可以再次呼叫它,以便繼續移除該值。 如果您有參考型別,則您必須提供要移除的物件。

[list[System.Management.Automation.PSDriveInfo]]$drives = Get-PSDrive
$drives.remove($drives[2])
$delete = $drives[2]
$drives.remove($delete)

如果可以從集合中尋找並移除項目,則 remove 方法會傳回 true

更多集合

還有許多其他可以使用的集合,但是有良好的泛型陣列替代品。 如果您想要深入瞭解更多選項,請查看此 Gist,那是由 Mark Kraus 合併在一起的。

其他細微差異

現在我已經討論過所有的主要功能,接下來我要提到幾件事,再進行總結。

預先調整大小的陣列

我說過,一旦建立陣列之後,您就無法變更它的大小。 我們可以使用 new($size) 建構函式來呼叫它,以建立預先決定大小的陣列。

$data = [Object[]]::new(4)
$data.count
4

相乘陣列

有趣的小技巧是,您可以將陣列乘以一個整數。

PS> $data = @('red','green','blue')
PS> $data * 3
red
green
blue
red
green
blue
red
green
blue

以 0 初始化

常見的情況是您想要建立全部為零的陣列。 如果您只會有整數,則強型別的整數陣列會預設為全部為零。

PS> [int[]]::new(4)
0
0
0
0

我們也可以使用相乘的技巧來執行此動作。

PS> $data = @(0) * 4
PS> $data
0
0
0
0

「相乘」技巧的好處是您可以使用任何值。 因此,如果您想要以 255 做為預設值,這會是很好的方式。

PS> $data = @(255) * 4
PS> $data
255
255
255
255

嵌套陣列

陣列中的陣列稱為「嵌套陣列」。 我不會在 PowerShell 中使用這些功能,但我在其他程式設計語言中使用了這些功能。 當您的資料符合資料格 (如模式) 時,請考慮在各陣列中使用一個陣列。

這裡提供兩種方式可以建立二維陣列。

$data = @(@(1,2,3),@(4,5,6),@(7,8,9))

$data2 = @(
    @(1,2,3),
    @(4,5,6),
    @(7,8,9)
)

在這些範例中,逗點非常重要。 我先前針對多行提供了一個一般陣列的範例,其中逗號是選擇性使用的。 多維陣列則不會發生這種情況。

我們使用索引標記法的方式現在有了些許變更,因為我們已經有了嵌套陣列。 使用上述 $data,這就是我們存取值 3 的方式。

PS> $outside = 0
PS> $inside = 2
PS> $data[$outside][$inside]
3

為每個陣列嵌套層級新增一組括弧。 第一組括弧是針對最外部的陣列,然後以您的方式從該處開始作業。

Write-Output -NoEnumerate

PowerShell 習慣取消換行或列舉陣列。 這是 PowerShell 使用管道的核心層面,但有時您不希望出現這種情況。

我通常會將物件輸送至 Get-Member 以便進行深入瞭解。 當我使用管道將陣列輸送給它時,它會取消換行,而 Get-Member 會看到陣列的成員,而不是實際的陣列。

PS> $data = @('red','green','blue')
PS> $data | Get-Member
TypeName: System.String
...

若要避免陣列取消換行,您可以使用 Write-Output -NoEnumerate

PS> Write-Output -NoEnumerate $data | Get-Member
TypeName: System.Object[]
...

我還有第二種方式,也就是駭客入侵的手法 (而且我試著避免這類的駭客入侵守法)。 您可以將逗號放在陣列前方,然後再使用管道輸送。

PS> ,$data | Get-Member
TypeName: System.Object[]
...

傳回陣列

當您從函式輸出或傳回值時,也會發生此陣列取消換行的情況。 如果您將輸出指派給變數,但這通常不是問題,您仍然可以取得陣列。

Catch 是您有新的陣列。 如果這樣造成困擾,您可以使用 Write-Output -NoEnumerate $arrayreturn ,$array 來解決問題。

還想知道什麼嗎?

我知道這裡有很多內容需要吸收消化。 我希望您每次閱讀本文時,都學到一些東西,而且本文將會是您未來漫長歲月中的絕佳參考資料。 若您認為本文內容實用,請與您認為可能會從中獲得價值的其他人分享。

從這裡開始,我會建議您查看我所寫的關於雜湊表的類似文章。