问题
Powershell's array notation has rather bizarre, albeit documented, behavior for slicing the end of arrays. This section from the official documentation sums up the bizarreness rather well:
Negative numbers count from the end of the array. For example, "-1" refers to the last element of the array. To display the last three elements of the array, type:
$a[-3..-1]
However, be cautious when using this notation.
$a[0..-2]
This command does not refer to all the elements of the array, except for the last one. It refers to the first, last, and second-to-last elements in the array.
The following code confirms the bizarreness:
$a = 0,1,2,3
$a[1..-1]
Which indeed outputs this bizarre result:
1
0
3
So, the question is, what is the idiomatic way to slice with one index relative the start and another relative the end of the array?
Please tell me it's something better than this ugly mess:
$a[1..($a.Count-1)]
Edit:
Another way to describe what I'm looking for is this: The idiomatic Powershell equivalent of this python expression:
a=1,2,3,4
a[1:-1]
Which, of course, evaluates to (2,3)
回答1:
If you want to get n elements from the end of an array simply fetch the elements from -n to -1:
PS C:\> $a = 0,1,2,3 PS C:\> $n = 2 PS C:\> $a[-$n..-1] 2 3
Edit: PowerShell doesn't support indexing relative to both beginning and end of the array, because of the way $a[$i..$j]
works. In a Python expression a[i:j]
you specify i
and j
as the first and last index respectively. However, in a PowerShell ..
is the range operator, which generates a sequence of numbers. In an expression $a[$i..$j]
the interpreter first evaluates $i..$j
to a list of integers, and then the list is used to retrieve the array elements on these indexes:
PS C:\> $a = 0,1,2,3 PS C:\> $i = 1; $j = -1 PS C:\> $index = $i..$j PS C:\> $index 1 0 -1 PS C:\> $a[$index] 1 0 3
If you need to emulate Python's behavior, you must use a subexpression:
PS C:\> $a = 0,1,2,3 PS C:\> $i = 1; $j = -1 PS C:\> $a[$i..($a.Length+$j-1)] 1 2
回答2:
Although not as neat as you might want but is cleaner in the way PowerShell works ...
(@(1,2,3,4)) | Select-Object -Skip 1
returns ...
2
3
4
回答3:
This could be the most idiomatic way to slice an array with both of its ends:
$array[start..stop] where stop is defined by taking the length of the array minus a value to offset from the end of the array:
$a = 1,2,3,4,5,6,7,8,9
$start = 2
$stop = $a.Length-3
$a[$start..$stop]
This will return 3 4 5 6 7
The start value starts counting with zero, so a start value of '2' gives you the third element of the array. The stop value is calculated with ($a.Length-3), this will drop the last two values because $a.Length-3 itself is included in the slice.
I have defined $start and $stop for clarity, obviously you can also write it like this:
$a = 1,2,3,4,5,6,7,8,9
$a[2..($a.Length-3)]
This will also return 3 4 5 6 7
回答4:
If you are looking for, say, the first three and last three elements in an array, with the results in an array, a little array addition will take care of the need.
[array]$A = (([int][char]'A')..([int][char]'Z')) | ForEach-Object {[char]$_}
$B = $A[0..2]+$A[-3..-1]
Clear-Host
Write-Host "Original List"
Write-Host $A -NoNewline -Separator ', '
Write-Host
Write-Host "First three and last three"
Write-Host $B -NoNewline -Separator ', '
Yields:
Original List
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
First three and last three
A, B, C, X, Y, Z
回答5:
Okay, so it's ugly as hell but I found that [1..99] or [1..999] works. Do not, however, use [1..999999] as PowerShell generates an array of values first before deciding that only the first five matter, the last one takes significant time to pre generate a million element array.
This is great for certain scripting scenarios, horrible for production code.
回答6:
I believe this is the right way to do it. all other methods require more code.
$a[1..($a.Count-1)]
Also, if array is converted to string it becomes easy to get data as below:
$a = 0,1,2,3
[string]::Concat($a).Substring(1)
回答7:
$arr = @(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
$arr | Select-Object -First 5 | Select-Object -Index (@(0..4) | Where-Object { $_ % 2 -eq 0})
$arr | Select-Object -Last 5
$arr | Select-Object -Unique
$arr | Sort-Object | Select-Object -Unique
$arr | Where-Object { $_ % 5 -eq 0 } | Sort-Object | Select-Object -Unique
$arr | Select-Object -First ($arr.Count - 3)
Actually code speaks for itself. I event don't need to explain.
However, 1) Provide the first five elements, but each second of those five. Equal to arr[:5:2] in Python 2) Get the last five elements. 3) Gives unique elements 4) Firstly sort and then provide unique 5) Gives only elements which equal 0 by applying modulo of 5, sort, unique. 5) Provide the first count of elements in that array minus three elements only.
来源:https://stackoverflow.com/questions/28460208/what-is-the-idiomatic-way-to-slice-an-array-relative-to-both-of-its-ends