为什么处理块正确返回一个哈希表,而不是一个哈希表数组?

huangapple go评论60阅读模式
英文:

Why do process blocks correctly return a hashtable, instead of an array of hashtables?

问题

我正在尝试对一些REST API响应进行排序并从中提取数据,并找到了这个有用的答案,告诉如何最终得到一个散列表。

$res= (Invoke-RestMethod @commonParams -uri "foo" -Method GET -FollowRelLink) | ForEach-Object { $_ } | Select-Object -Property id, filename | ForEach-Object -begin {$h=@{}} -process {$h[$_.filename] = $_.id} -end {$h}

好的,所以我想把它简化一点,去掉开始/处理/结束块,像这样:

$res = (Invoke-RestMethod @commonParams -uri "foo" -Method GET -FollowRelLink) | ForEach-Object { $_ } | Select-Object -Property id, filename | ForEach-Object { $h=@{}; $h[$_.filename] = $_.id; $h }

一切都好,现在我应该能够使用方括号引用每个项目的键值,但这并不起作用,只能使用点符号引用。

所以我去查找并找到了这个答案,结果是正确的:

$res.GetType()

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

但即使从通用的散列表构造函数更改为使用.Add()方法也没有帮助:

$res = (Invoke-RestMethod @commonParams -uri "foo" -Method GET -FollowRelLink) | ForEach-Object { $_ } | Select-Object -Property id, filename | ForEach-Object { $h=@{}; $h.add($_.filename, $_.id); $h }

$res.GetType()

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

我重新添加了处理块:

$res = (Invoke-RestMethod @commonParams -uri "$foo" -Method GET -FollowRelLink) | ForEach-Object { $_ } | Select-Object -Property id, filename | ForEach-Object -begin {$h=@{}} -process {$h[$_.filename] = $_.id} -end {$h}

$res.GetType()

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

一切都按预期工作。

所以我想知道,如果可能的话,当我使用处理块时到底发生了什么,而不是在管道中创建“假”的多行函数?

如果有人在意的话,我实际上完全避免了上述问题,使用了这里的 Group-Object

英文:

I am trying to sort some REST API responses and extract data from them, and found this helpful answer on how to end up with a hashtable at the end.

$res= (Invoke-RestMethod @commonParams -uri "foo" -Method GET -FollowRelLink) | ForEach-Object { $_ } | Select-Object -Property id, filename | ForEach-Object -begin {$h=@{}} -process {$h[$_.filename] = $_.id} -end {$h}

Great, so I thought I'd shorten it a bit by getting rid of the begin/process/end blocks like so:

$res = (Invoke-RestMethod @commonParams -uri "foo" -Method GET -FollowRelLink) | ForEach-Object { $_ } | Select-Object -Property id, filename | ForEach-Object { $h=@{}; $h[$_.filename] = $_.id; $h }

All good, I should now be able to reference each item by it's key value using square brackets, but that doesn't work, only referencing by dot notation.

So I went searching and found this answer, which turned out to be correct:

$res.GetType()

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

But, even changing from the generic hashtable constructor to using the .Add() method didn't help:

$res = (Invoke-RestMethod @commonParams -uri "foo" -Method GET -FollowRelLink) | ForEach-Object { $_ } | Select-Object -Property id, filename | ForEach-Object { $h=@{}; $h.add($_.filename, $_.id); $h }

$res.GetType()

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

I add the process blocks back in:

$res = (Invoke-RestMethod @commonParams -uri "$foo" -Method GET -FollowRelLink) | ForEach-Object { $_ } | Select-Object -Property id, filename | ForEach-Object -begin {$h=@{}} -process {$h[$_.filename] = $_.id} -end {$h}

$res.GetType()

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

And it's all working as expected.

So I'd like to know, if possible, what exactly is happening when I use the process blocks as opposed to just making 'fake' multi-line functions in the pipeline?

And if anyone cares, I actually ended up avoiding the above issue completely by using Group-Object from here.

答案1

得分: 2

ForEach-Object 模拟了 脚本块管道 中的工作方式。第一个和最后一个片段能按预期工作并返回哈希表是因为 -Begin-End,而不是因为 -Process(默认执行且是强制参数)。如果不使用 -Begin,则会简单地为每个输入对象创建和输出一个新的哈希表。

  • 使用 ForEach-Object
0..10 | ForEach-Object -Begin {
    "仅在处理第一个输入对象之前执行一次"
} -Process {
    "处理每个输入对象:$_"
} -End {
    "仅在处理最后一个输入对象之后执行一次"
}
  • 使用脚本块
0..10 | & {
    begin {
        "仅在处理第一个输入对象之前执行一次"
    }
    process {
        "处理每个输入对象:$_"
    }
    end {
        "仅在处理最后一个输入对象之后执行一次"
    }
}

about_Functions_Advanced_Methods 解释了这些块的工作原理。

另外,您的代码可以简化为以下内容:

$h = @{}
Invoke-RestMethod @commonParams -uri "foo" -Method GET -FollowRelLink |
    ForEach-Object { $h[$_.filename] = $_.id }

或者使用 Group-Object -AsHashTable,正如您已经找到的:

$h = Invoke-RestMethod @commonParams -uri "foo" -Method GET -FollowRelLink |
    Group-Object FileName -AsHashTable

在这种情况下,如果来自 Invoke-RestMethod 的输出已经被枚举,则可能不需要 Write-Output

英文:

ForEach-Object emulates how script blocks work in the pipeline. The reason why the first and last snippets work as expected, returning a hashtable is because -Begin and -End not because of -Process which executes by default and is a mandatory parameter. If you don't use -Begin you would simply be creating and outputting a new hashtable per input object.

  • Using ForEach-Object
0..10 | ForEach-Object -Begin {
    "executes only once, before the first input object is processed"
} -Process {
    "processes each input object: $_"
} -End {
    "executes only once, after the last input object is processed"
}
  • Using a script block
0..10 | & {
    begin {
        "executes only once, before the first input object is processed"
    }
    process {
        "processes each input object: $_"
    }
    end {
        "executes only once, after the last input object is processed"
    }
}

about_Functions_Advanced_Methods explain how these blocks work.

Also your code could be simplified to this:

$h = @{}
Invoke-RestMethod @commonParams -uri "foo" -Method GET -FollowRelLink |
    Write-Output |
    ForEach-Object { $h[$_.filename] = $_.id }

Or using Group-Object -AsHashTable as you have already found:

$h = Invoke-RestMethod @commonParams -uri "foo" -Method GET -FollowRelLink |
    Write-Output |
    Group-Object FileName -AsHashTable

Write-Output in this case may not be needed if the output from Invoke-RestMethod is already enumerated.

huangapple
  • 本文由 发表于 2023年2月16日 10:43:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/75467328.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定