63

Powershell 的数组表示法在切片数组末尾时具有相当奇怪的行为,尽管已记录在案。官方文档中的这一部分很好地总结了这个奇异之处:

负数从数组末尾开始计数。例如,“-1”指的是数组的最后一个元素。要显示数组的最后三个元素,请键入:

$a[-3..-1]

但是,使用此表示法时要小心。

$a[0..-2]

此命令不引用数组的所有元素,除了最后一个元素。它指的是数组中的第一个、最后一个和倒数第二个元素。

以下代码证实了这种奇怪之处:

$a = 0,1,2,3
$a[1..-1]

这确实输出了这个奇怪的结果:

1
0
3

所以,问题是,用一个相对于数组开头的索引和另一个相对于数组结尾的索引进行切片的惯用方法 是什么?

请告诉我它比这个丑陋的烂摊子更好:

$a[1..($a.Count-1)]

编辑:

描述我正在寻找的另一种方法是:这个 python 表达式的惯用 Powershell 等效项:

a=1,2,3,4
a[1:-1]

当然,评估为(2,3)

4

8 回答 8

63

如果要从数组末尾获取n 个元素,只需从-n-1获取元素:

PS C:\> $a = 0,1,2,3 
PS C:\> $n = 2 
PS C:\> $a[-$n..-1]
2
3

编辑: PowerShell 不支持相对于数组的开头结尾的索引,因为它的工作方式$a[$i..$j]。在 Python 表达式中a[i:j],您分别指定ij作为第一个和最后一个索引。但是,在 PowerShell..中是范围运算符,它生成一个数字序列。在表达式$a[$i..$j]中,解释器首先计算$i..$j出一个整数列表,然后该列表用于检索这些索引上的数组元素:

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

如果您需要模拟 Python 的行为,则必须使用子表达式:

PS C:\> $a = 0,1,2,3 
PS C:\> $i = 1; $j = -1 
PS C:\> $a[$i..($a.Length+$j-1)]
1
2
于 2015-02-11T18:00:15.013 回答
23

虽然不像您想要的那样整洁,但 PowerShell 的工作方式更简洁......

(@(1,2,3,4)) | Select-Object -Skip 1

返回...

2
3
4
于 2017-08-24T11:46:33.177 回答
4

结合Select-Object -SkipSelect-Object -SkipLast喜欢:

$a = 0,1,2,3
$a | Select-Object -Skip 1 | Select-Object -SkipLast 1

回报:

1
2

不像 Python 那样优雅,但至少您不必使用Countor Length,这意味着如果数组未存储在变量中,这也可以工作。

于 2020-01-09T19:19:41.283 回答
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

于 2017-06-21T14:17:09.803 回答
2
$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)

实际上,代码不言自明。我事件不需要解释。

然而,

  1. 提供前五个元素,但要提供这五个元素中的每一秒。等于 Python 中的 arr[:5:2]
  2. 获取最后五个元素。
  3. 赋予独特的元素
  4. 先排序再提供唯一
  5. 通过应用 5 的模,排序,唯一的,仅给出等于 0 的元素。
  6. 仅提供该数组中的第一个元素数减去三个元素。
于 2019-08-26T13:57:37.897 回答
2

例如,如果您正在寻找一个数组中的前三个和最后三个元素,并将结果放在一个数组中,那么添加一点数组就可以满足需要。

[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 ', '

产量:

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
于 2016-01-18T19:13:34.497 回答
0

不幸的是,将您的阵列管道输送到Select-Object -Skip $skipStart | Select-Object -SkipLast $skipEnd是获取正确项目的唯一万无一失的惯用方式。

(@wensveen 率先采用了这种策略,但 SO 不允许我发表评论,我认为给出解释需要完整回复的详细信息。)

计算范围不起作用

如果您的列表比您预期的要短,范围从$a[3..$a.count-3]等不起作用:假设$a有 4 个项目,您最终得到的范围是3..1,即@(3, 2, 1)3 个倒序的项目,而在 Python 示例a[3:-3]中将返回零个元素,因为从开头开始三个到结尾三个结束的升序范围是空的。(在 Python 中,范围顺序是一个额外的显式参数,a[-1:1:-1]允许反向排序的结果。)

输出格式

像往常一样,如果您需要强制输出为数组,您可以将管道包装在@(...)数组强制中。@RiverHeart 提到想要保留输入数组类型 eg Int32[],但据我所知,这也不适用于普通索引,我可能会遗漏一些东西。

获得正确的物品

另一个简短的提及:您使用的数字与范围表示法相比,跳过操作略有不同。如果您从索引 1 开始,那么您也会从零索引数组的开头跳过 1 项,因此它们是相同的;但是对于倒数第二个元素,在末尾跳过 1 等效于 index-2或同样。$a.count-2

添加数组索引

最后,将范围文字添加在一起是一个不错的功能。由于上述原因,它没有概括,它不会阻止您意外获得相同的项目两次,例如,如果您的范围重叠,但能够$a[0..2+-3..-1]在交互式提示下获得前两个和最后一个很好三,比制作和组合单独的数组更简洁,仍然很清楚。我想如果你想对 Python 嗤之以鼻,那是它做起来不那么简单的一件事!

于 2022-01-31T06:08:33.020 回答
0

我相信这是正确的做法。所有其他方法都需要更多代码。

$a[1..($a.Count-1)]

此外,如果将数组转换为字符串,则可以轻松获取如下数据:

$a = 0,1,2,3
[string]::Concat($a).Substring(1)
于 2018-05-01T20:32:47.593 回答