通过哈希循环,或在PowerShell中使用数组

通过哈希循环,或在PowerShell中使用数组,powershell,dictionary,hashtable,Powershell,Dictionary,Hashtable,我正在使用这段(简化的)代码从SQL Server中提取一组表 它工作得很好,但现在有些表需要通过视图提取,有些则不需要。所以我需要这样的数据结构: "Page" "vExtractPage" , "ChecklistItemCategory" "ChecklistItemCategory" , "ChecklistItem" "vExtractChecklistItem" 我在看散列,但没有找到任何关于如何循环散列的内容。在这

我正在使用这段(简化的)代码从SQL Server中提取一组表

它工作得很好,但现在有些表需要通过视图提取,有些则不需要。所以我需要这样的数据结构:

"Page"                      "vExtractPage"
, "ChecklistItemCategory"   "ChecklistItemCategory"
, "ChecklistItem"           "vExtractChecklistItem"
我在看散列,但没有找到任何关于如何循环散列的内容。在这里做什么才是正确的?也许只是使用一个数组,但两个值都用空格分隔


还是我遗漏了一些明显的东西?

关于循环散列:

$Q = @{"ONE"="1";"TWO"="2";"THREE"="3"}
$Q.GETENUMERATOR() | % { $_.VALUE }
1
3
2

$Q.GETENUMERATOR() | % { $_.key }
ONE
THREE
TWO

Christian的答案很好,它展示了如何使用
GetEnumerator
方法循环遍历每个哈希表项。还可以使用
属性循环。以下是一个示例:

$hash = @{
    a = 1
    b = 2
    c = 3
}
$hash.Keys | % { "key = $_ , value = " + $hash.Item($_) }
输出:

key = c , value = 3
key = a , value = 1
key = b , value = 2
c: 3
b: 2
a: 1
c: 3
b: 2
a: 1
Key = c and Value = 1
Key = b and Value = 2
Key = a and Value = 3
Key = a and Value = 3
Key = b and Value = 2
Key = c and Value = 1

脚本不首选速记;它的可读性较差。%{}运算符被视为速记。以下是如何在脚本中实现可读性和可重用性:

变量设置 选项1:GetEnumerator() 注:个人喜好;语法更容易阅读

GetEnumerator()方法将按如下所示执行:

foreach($hash.GetEnumerator()中的h){
写入主机“$($h.Name):$($h.Value)”
}
输出:

key = c , value = 3
key = a , value = 1
key = b , value = 2
c: 3
b: 2
a: 1
c: 3
b: 2
a: 1
Key = c and Value = 1
Key = b and Value = 2
Key = a and Value = 3
Key = a and Value = 3
Key = b and Value = 2
Key = c and Value = 1
选项2:钥匙 密钥方法将按如下所示执行:

foreach($hash.Keys中的h){
写入主机“${h}:$($hash.Item($h))”
}
输出:

key = c , value = 3
key = a , value = 1
key = b , value = 2
c: 3
b: 2
a: 1
c: 3
b: 2
a: 1
Key = c and Value = 1
Key = b and Value = 2
Key = a and Value = 3
Key = a and Value = 3
Key = b and Value = 2
Key = c and Value = 1
补充资料 请小心对哈希表进行排序

可以将其更改为数组:

PS> $hash.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Hashtable                                System.Object


PS> $hash = $hash.GetEnumerator() | Sort-Object Name
PS> $hash.GetType()

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


如果您使用的是PowerShell v3,则可以使用JSON而不是哈希表,并将其转换为具有以下特性的对象:


您也可以在不使用变量的情况下执行此操作

@{
‘foo’=222
'巴'=333
‘baz’=444
“qux”=555
}|%getEnumerator |%{
$\u0.key
$\美元价值
}

这里是另一种快速方法,只需使用键作为哈希表的索引即可获得值:

$hash = @{
    'a' = 1;
    'b' = 2;
    'c' = 3
};

foreach($key in $hash.keys) {
    Write-Host ("Key = " + $key + " and Value = " + $hash[$key]);
}

我更喜欢在带有管道的枚举器方法上使用此变量,因为您不必在foreach中引用哈希表(在PowerShell 5中测试):

输出:

key = c , value = 3
key = a , value = 1
key = b , value = 2
c: 3
b: 2
a: 1
c: 3
b: 2
a: 1
Key = c and Value = 1
Key = b and Value = 2
Key = a and Value = 3
Key = a and Value = 3
Key = b and Value = 2
Key = c and Value = 1
现在,这并不是故意按值排序的,枚举器只是以相反的顺序返回对象

但由于这是一个管道,我现在可以根据值对从枚举器接收的对象进行排序:

$hash.getEnumerator() | sort-object -Property value -Desc | foreach {
  Write-Host ("Key = " + $_.key + " and Value = " + $_.value);
}
输出:

key = c , value = 3
key = a , value = 1
key = b , value = 2
c: 3
b: 2
a: 1
c: 3
b: 2
a: 1
Key = c and Value = 1
Key = b and Value = 2
Key = a and Value = 3
Key = a and Value = 3
Key = b and Value = 2
Key = c and Value = 1

也可以使用子表达式运算符$()进行短遍历,该运算符返回一个或多个语句的结果

$hash = @{ a = 1; b = 2; c = 3}

forEach($y in $hash.Keys){
    Write-Host "$y -> $($hash[$y])"
}
结果:

a -> 1
b -> 2
c -> 3

为了可读性,特别是对于像我这样不专注于powershell的开发人员,我同意这种方法更容易阅读,因此可能更适合编写脚本。除了可读性,VertigoRay的解决方案和Andy的解决方案有一个区别{}是
ForEach对象
的别名,与此处的
ForEach
语句不同
ForEach对象
利用了管道,如果您已经在使用管道,那么管道可以更快。
foreach
语句不存在;这是一个简单的循环。@JamesQMurphy我从上面复制并粘贴了@andy arismendi提供的答案;对于这个问题,它是流行的管道解决方案。你的陈述使我最感兴趣,那就是
ForEach对象
(又名:
%{}
)比
ForEach
快;我证明了这并不是更快。我想证明你的观点是否正确;因为我,像大多数人一样,喜欢我的代码以尽可能快的速度运行。现在,您的论点变为并行运行作业(可能使用多台计算机/服务器)。。。当然,这比从单个线程运行一个串联作业要快。干杯@JamesQMurphy我还认为powershell遵循了shell在多个进程之间传输数据流时所提供的传统优势:每个管道阶段都是一个独立的进程,这一简单事实使您获得了自然的并行性(当然,缓冲区可能会耗尽,最终整个管道的运行速度与最慢的进程一样慢)。这不同于管道进程自行决定生成多个线程,这完全取决于进程本身的实现细节。注意:该命令是从Json转换而来的(反之,ConvertTo Json)——只需交换破折号位置。这会得到一个“ForEach对象:无法绑定参数'Process'。无法将“System.String”类型的“getEnumerator”值转换为“System.Management.Automation.ScriptBlock”类型“如何以其他顺序枚举散列?例如,当我想以升序(此处为a…b…c)打印其内容时。这可能吗?为什么
$hash.Item($\u)
而不是
$hash[$\u]
?@LPrc使用Sort Object方法来实现这一点。本文对此做了很好的解释:您甚至可以只使用
$hash.$\u
。如果hashtable包含key='Keys',此方法不起作用。枚举器可以在“any”中返回值顺序,因为它只是迭代底层实现。在本例中,它恰好出现相反的顺序。对于一个计数器示例:
$x=@{a=1;z=2};$x.q=3
在这里是按q,a,z“排序的”。
[ordered]
属性触发对不同实现的选择。