PowerShell从数组中删除项目[0]

32

我有些困难,想要移除数组的第一行(项目ID)。

$test.GetType()

IsPublic IsSerial Name                                     BaseType                                                                                                      
-------- -------- ----                                     --------                                                                                                      
True     True     Object[]                                 System.Array

为了列出我尝试的所有选项,我使用了,$test | gm,它明确说明:

Remove         Method                void IList.Remove(System.Object value)                                                                                              
RemoveAt       Method                void IList.RemoveAt(int index)

所以当我尝试$test.RemoveAt(0)时,我会得到错误:

Exception calling "RemoveAt" with "1" argument(s): "Collection was of a fixed size."At line:1 char:1
+ $test.RemoveAt(1)
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : NotSupportedException

所以我最终发现这里我的数组需要是System.Object类型才能使用$test.RemoveAt(0)。在脚本开头声明所有数组为列表是最佳实践吗?还是在需要此功能时将数组转换为列表,例如使用$collection = ({$test}.Invoke())?这两种类型的优缺点是什么?感谢您的帮助。
12个回答

59

1
这是目前为止最好的解决方案!我从现在开始将使用它,谢谢。 - DarkLite1
2
如果您希望$rest具有一致的数据类型,则此方法不可靠。如果$arr仅有2个元素,则$rest将是标量,否则可能是数组甚至是null。 - coldfix
2
除非$rest已经是一个数组类型,否则它将保持为一个非数组类型。所以$first,$arr = $arr应该可以正常工作。 你也可以通过以下方式强制转换:$first,[object[]]$rest = $arr - Willem M.
如果$arr只有0或1个元素,则此方法无效。例如,$arr=@(1); $first, $arr = $arr; $arr.gettype()会将$arr解释为空,而不是@()。同样,当$arr有两个元素时,它可能无法正常工作,因为正如注释#2所说,它会导致$arr成为一个标量值。 - puravidaso
这个不起作用。将其应用于一个有两个元素的数组。你将不再拥有一个数组。这是一个严重的失败。 - undefined
显示剩余2条评论

34

数组是固定大小的,就像错误提示所说的那样。RemoveAt() 是一个继承的方法,不适用于普通数组。要删除数组中的第一项,您可以通过复制每个项目但不包括第一个项目的副本来重写数组,例如:

数组是固定大小的,就像错误提示所说的那样。RemoveAt() 是一个继承的方法,不适用于普通数组。要删除数组中的第一项,您可以通过复制每个项目但不包括第一个项目的副本来重写数组,例如:

$arr = 1..5

$arr
1
2
3
4
5

$arr = $arr[1..($arr.Length-1)]

$arr
2
3
4
5
如果您需要删除不同索引处的值,则应考虑使用 List。它支持 Add()Remove()RemoveAt():

如果需要在不同的索引处移除值,则应考虑使用 List。它支持 Add()Remove()RemoveAt():

#If you only have a specific type of objects, like int, string etc. then you should edit `[System.Object] to [System.String], [int] etc.
$list = [System.Collections.Generic.List[System.Object]](1..5)

$list
1
2
3
4
5

$list.RemoveAt(0)

$list
2
3
4
5

有关数组工作原理的更多详细信息,请参见我的早期SO答案about_Arrays


2
$arr[1..($arr.Length-1)] does not work for arrays of length 1 (one would expect it to return empty array, but it does not). Use this if that may be a problem: $upperBound = (1, ($args.length-1) | measure-object -max).maximum; $userProvidedArgs = $args[1..$upperBound] - t.animal
1
好主意。我会尝试 $arr[1..([math]::max(1,($arr.length-1)))] - Frode F.
3
我建议使用$arr[1..$arr.length],这不仅更短,而且在$arr只有一个元素的情况下也能更好地工作。然后你的方法就不会剥离元素,而这个方法会。 - coldfix
这是一个非常有趣的解决方案,将其转换为列表(很容易)......我想这将是一个很好的解决方案,与强制转换一起协作(当我们知道我们想要的确定对象)以进一步处理 .net 库代码... - ZEE

23
您可以使用 Select-Object -Skip <count> 命令来省略前 count 个项目:
PS C:\> 1..3 | Select-Object -Skip 1
2
3
PS C:\>

PS C:\> 1 | Select-Object -Skip 1
PS C:\>

谢谢,这正是我在寻找的。使用-Skip似乎比调整长度和索引更容易阅读。 - Tatiana Racheva
1
我非常喜欢这个建议,因为它可以提高代码的清晰度,但是这种方法给我带来了一些麻烦:当数组有两个对象时,结果不是一个包含一个对象的数组,而是剩下的最后一个对象;当数组只有一个对象时,结果是 $null - Zé Cláudio
3
嗨@ZéCláudio,PowerShell的默认行为是将单项序列展平为标量,并将空序列展平为$null。 为了在所有情况下强制使用数组,请将序列包装在@(...)数组子表达式运算符中。 - Ron MacNeil

19

这将允许您从数组中删除任意元素的每个出现,而无需使用更复杂的 .NET 对象。

$x=<array element to remove>
$test = $test | Where-Object { $_ -ne $x }

或者,

$x=<array index to remove>
$test = $test | Where-Object { $_ -ne $test[$x] }

这样做会执行相同的操作,但仅会删除其中一个元素。如果有重复的元素,它们将保留下来。这也可以通过上述索引方式来完成。

$x=<array element to remove>
$skip=$true
$test = $test | ForEach-Object { if (($_ -eq $x) -and $skip) { $skip=$false } else { $_ } }

还根据其中的评论,如果你不确定$test是一个数组,最好先使用$test=@($test)


点赞,这对我来说起了作用。而且它很简洁。 - Spikee
1
如果数组包含重复的值,这可能会表现不正确。 - Ron MacNeil
1
请注意:当只剩下一个元素时,类型为String而不是Object[]!可以通过将以下内容添加到示例的末尾来修复代码:if ($test -is [string]) { # it's a string when it has only 1 item $test= ,$tes t# make it an array again (with only 1 item) } - Jan
太棒了!简单而有效! - Oleh Tarasenko

5

更新一下 - 关于@Frode F.的回答存在问题

如果数组中元素的数量大于1

$arr = $arr[1..($arr.Length-1)]

如果元素数量为1,则不会移除该元素。
if($arr.Length -le 1) {
    $arr = @()
}
else {
    $arr = $arr[1..($arr.length - 1)]
}

3

我认为这取决于具体情况。如果你只需要一次删除第一个元素,那么可以使用数组切片:

$arr = $arr[1..($arr.length-1)]

如果你要重复执行这个操作,那么你应该从arraylist或者通用集合开始。如果是一个大数组,你可能想把创建它的表达式放到scriptblock中,并在其上执行.invoke(),而不是让管道创建一个数组,然后将其转换为集合。


2

removeat() 适用于数组列表(如 $error):

[collections.arraylist]$a = get-process
$a.removeat(0)

0

如果我们有一个必须分部分执行的大型数组(或ArrayList)的情况 - 我使用了一些小技巧:

#$bigArray with thousands of items
while($bigArray.Count -gt 0)
{
    if($bigArray.Count -gt 100)
    {
    $k = 100
    }else {$k = $bigArray.Count}
    $part = $bigArray | select -First $k
#now we can make some operations with this $part
#in the end of loop we should exclude this part from big array
    if($bigArray.Count -gt 100)
    {
        $bigArray = $bigArray | select -Last ($bigArray.Count - $k)
    }else {$bigArray = @()}#in this step we have been handle last part of array and we should null him for stop loop
}

现在我们可以通过分段(每次100个项目)来处理大数组


0

抱歉回复晚了,我也在这方面苦苦挣扎。针对我的意图和目的(写入文本文件),我意识到由于数组是固定大小的,所以我可以将其值设置为空字符串而不是删除它。

$results = SQLQuery -connectionString $connectionString  -query $query;
$results[0] = '';
foreach ($r in $results) {
    Add-Content $skus $r[0]; 
}

对我来说,这样做可以去掉我在平面文件中不想要的标题。希望这能帮助其他人。


这并不会从数组中删除任何内容,这也是问题的目的。 - Slogmeister Extraordinaire

0

如果您想根据已知的索引 x (基于0)删除一行,则也可以只复制数组的较低部分和较高部分。

从数组中删除第x行

$LastElement = $arry.GetUpperBound(0)

if ($LastElement -ge 0) {

    if ($x -eq 0) {
        $lowerRange = @()
    } else {
        $lowerRange = @($arry[0..($x-1)])
    }

    if ($x -eq $LastElement) {
        $upperRange = @()
    } else {
        $upperRange = @($arry[($x+1)..$LastElement])
    }

    $arry = @($lowerRange; $upperRange)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接