【问题标题】:Unable to remove item from hash table无法从哈希表中删除项目
【发布时间】:2016-11-30 16:16:00
【问题描述】:

在 Powershell 中,我有一个包含与此类似的数据的哈希表 -

Name                   Value                                           
----                   -----                                           
1-update.bat           1                                               
2-update.bat           2                                               
3-update.bat           3                                               
3.1-update.bat         3.1                                             
4-update.bat           4    

我还有一个包含数字的变量,例如3

我想做的是遍历数组并删除值小于或等于3的任何条目

我认为这很容易,尤其是the docs 说有表包含.remove 方法。但是,我下面的代码失败了,产生了这个错误-

使用“1”个参数调用“Remove”的异常:“Collection was of a 固定大小。”

这是我使用的代码 -

$versions = @{}
$updateFiles | ForEach-Object {
    $versions.Add($_.name, [decimal]($_.name -split '-')[0])
}

[decimal]$lastUpdate = Get-Content $directory\$updatesFile

$versions | ForEach-Object {
    if ( $_.Value -le $lastUpdate ) {
        $versions.Remove($version.Name)
    }
}

我首先尝试以不同的方式循环$versions,尝试了foreachfor 两种方法,但都以相同的方式失败。

我还尝试创建一个临时数组来保存要删除的版本的名称,然后循环该数组以删除它们,但这也失败了。

接下来我点击谷歌,虽然我可以找到几个类似的问题,但没有一个能回答我的具体问题。大多数情况下,他们建议使用列表 (New-Object System.Collections.Generic.List[System.Object]),据我所知,这对我没有帮助。

有人能提出解决办法吗?

【问题讨论】:

  • 那是你的实际代码吗? $version 是从哪里来的 $version.Name 子句?

标签: powershell hashtable


【解决方案1】:

在这里,你可以使用 .Remove(),你只需要一个哈希表的克隆,这样它就可以让你在枚举时删除项目。

[hashtable]$ht = @{ '1-update.bat'=1;'2-update.bat'=2;'3-update.bat'=3;'3.1-update.bat'=3.1; '4-update.bat'=4    }

#Clone so that we can remove items as we're enumerating
$ht2 = $ht.Clone()
foreach($k in $ht.GetEnumerator()){ 
    if([decimal]$k.Value -le 3){
        #notice, deleting from clone, then return clone at the end
        $ht2.Remove($k.Key) 
    } 
}


$ht2

请注意,我也将原始变量转换为明确的哈希表,这可能不是必需的,但我喜欢这样做至少可以让事情变得清晰。

【讨论】:

  • +upvote 提到枚举时不能删除,非常重要
  • 这行得通,但必须克隆对象真的很痛苦。似乎原本应该很简单的事情,却被 MS 弄得极其困难。谢谢。
【解决方案2】:

您似乎只是将ForEach-Objectforeach 混淆了,但只是混淆了一半(可能之前是foreach,而您已转换了它)。

您不能将[hashtable] 直接发送到ForEach-Object;在这种情况下,$_ 将仅指您发送的单个 [hashtable]。您可以这样做:

foreach ($version in $versions.GetEnumerator()) {
    $version.Name
    $version.Value
}

或者你可以这样做:

$versions.Keys | ForEach-Object {
    $_  # the name/key
    $versions[$_]  # the value
    $versions.$_   # also the value
}

【讨论】:

  • 以前从未见过GetEnumerator() 方法,这很漂亮。对于他的案例,我认为更直接的代码示例是$versions.Keys | ?{$versions.$_ -le $lastUpdate}|%{$Versions.Remove($_)}
  • 嗯同意了,那个版本也可以很好的回答
  • @TheMadTechnician - 有人建议几乎与答案相同,但它没有用。恐怕你的建议也失败了,我已经看到了同样的“集合是固定大小”的错误。谢谢。
  • 啊,对不起,将第一部分包装在一个子表达式中,它可以正常工作:$($versions.Keys) | ?{$versions.$_ -le $lastUpdate}|%{$Versions.Remove($_)}。这样,它会在尝试修改哈希表之前输出整个键数组。
【解决方案3】:
$ht.Keys  # list all keys
$ht[$_] # take an element of hastable
$ht.Remove($_)  # remove an element of hastable by his key

你想要什么:

$ht.Keys | ? { $ht[$_] -le 3 } | %{$ht.Remove($_) }

【讨论】:

  • 请不要发布裸代码也提供一些解释它在做什么。
  • 谢谢,但这不起作用 - “索引操作失败;数组索引评估为空。”
【解决方案4】:

您需要创建一个临时数组来保存要删除的版本的名称/键,然后循环该数组以将它们从哈希表中删除:

$versionKeysToRemove = $versions.Keys | Where-Object { $versions[$_] -le $lastUpdate }
$versionKeysToRemove | ForEach-Object { $versions.Remove($_) }

或更短:

($versions.Keys | ? { $versions[$_] -le $lastUpdate }) | % { $versions.Remove($_) }

请注意括号。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-10
    • 2021-06-01
    • 2021-04-06
    • 2015-08-20
    • 2010-09-21
    • 1970-01-01
    • 1970-01-01
    • 2018-11-17
    相关资源
    最近更新 更多