-
Notifications
You must be signed in to change notification settings - Fork 7.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhance foreach
loops to allow iterating over multiple, parallel collections in tandem (pairs)
#14732
Comments
foreach
loops to allow iterating over multiple collections in tandemforeach
loops to allow iterating over multiple collections in tandem (pairs)
As this is already a valid syntax: $aElem, $bElem = 'One', 1 I would expect that the iterator assignment part (" $pairs = @(@('one', 1), @('two', 2), @('Three', 3))
foreach ($aElem, $bElem in $Pairs) {
'{0}: {1}' -f $aElem, $bElem
} If the iterator is indeed a single item: foreach ($aElem in $Pairs) {
'{0}: {1}' -f $aElem[0], $aElem[1]
} It should return an array, similar to (as it currently does): $aElem = 'One', 1 Meaning the last item in the iterator list should contain the rest of the array, similar to: $aElem, $bElem = 'One', 1, 'A' Where |
@iRon7, the It embodies an instruction for a (behind-the-scenes) assignment to the iterator variable in every loop iteration, namely to the current element of the enumeration. As such, there's no inherent requirement to support multiple iterator variables, the way true assignments support multiple target variables in destructuring assignments. What I meant to express in the OP is that allowing multiple iterator variables is inspired by regular destructuring assignments, in that they would function analogously, albeit with enforced symmetry: The number of iterator variables on the LHS would have to match the number of explicitly listed collections on the RHS: That is, the following would be syntactically valid: $a = 'foo', 'bar'
$b = 'baz', 'qux'
# OK: two iterator variables, 2 collections.
foreach ($aElem, $bElem in $a, $b) {
"$aElem, $bElem"
}
# Ditto
foreach ($aElem, $bElem in (Get-ChildItem foo/), (Get-ChildItem bar/)) {
"$aElem, $bElem"
} By contrast, |
@mklement0 Quick note: your proposed semantic for the LHS of PS> foreach ($i in 1,2,3) {$i}
1
2
3 (@iRon7 's suggestion would work and it's what I would have implemented if I'd had time since it naturally falls out of destructuring.) That said, the |
Thanks, @BrucePay, but I don't think there would be a collision, at least not technically:
I've updated the OP to make that clearer. If the consensus ends up being that this distinction is too subtle, a new syntax could be pondered, but personally I don't think it's necessary. |
I needed some time to think this through and enhance my $a = 'foo', 'bar'
$b = 'baz', @(1,2)
$c = 'and', 'so on'
$a |Join $b |Join $c |% {
$aElem, $bElem, $cElem = $_
"$aElem | $bElem | $cElem"
}
foo | baz | and
bar | 1 2 | so on See also: |
Re not "investing too much in traditional operators" (read: statements). There is no reason to pit cmdlet-based solutions against expression / statement-based solutions: both are necessary, and in certain cases use of one over the other is the only option. Instead, we should strive for feature parity, to the extent that is feasible. Therefore, this is again not an either-or scenario, so I suggest you add your previous example to #14994 |
Agree, I have rephrased it in the comment of this issue and removed it from the |
foreach
loops to allow iterating over multiple collections in tandem (pairs)foreach
loops to allow iterating over multiple, parallel collections in tandem (pairs)
Thinking outside the box and following the PowerShell's streaming concept... This: $Count = (1..3 |) Causes currently an error:
Instead it could possibly create a kind of a "deferred pipeline object" and each time the object is used/invoked it processes and returns the next item in the deferred pipeline (until it is empty where it returns an Wishful thinking: $Count = (1..3 |) # Initialize the deferred pipeline object
$Count
1
$Count
2
$Count
3
$Count # Nothing (`AutomationNull`) returns More specific: $a = (Get-Connect .\MyHughFile.txt |) # or any other long stream
$b = (1..1e9 |) # In the idea, the range shouldn't affect the member used for $b
$c = (Import-Csv .\Large.csv |)
$a | ForEach-Object {
Write-Host '$a item:' $_
Write-Host '$b item:' $b # Everytime the $b is used, it processes the next item in the deferred $b pipeline
Write-Host '$c item property:' $c.myProperty # Dito for $c
} (I am happy to do a separate propose for this but just want to check whether this idea makes any sense at all.) |
@iRon7, implementing an analogous feature for use streaming use, in the pipeline, makes sense to me (as in the I do suggest creating a separate issue for that. As a thought up front: I'm not sure we need new syntax for that, perhaps even a # WISHFUL THINKING
{ 0..2 }, { 'a'..'c' } | ForEach-Object -InvokeScriptBlocks { '{0}: {1}' -f $_[0], $_[1] }
0: a
1: b
2: c |
Sorry, I had the wrong tab open before. I discussed this with the Engine working group and we don't think this should be implemented at the language/syntax level. Instead it's something that LINQ, Python and other languages provide as a method or function. A function for that in PowerShell could be implemented in an external module first before we evaluate whether it should be included in PowerShell itself. |
|
Summary of the new feature/enhancement
Iterating over multiple collections that have corresponding elements iteratively is a common scenario.
This is currently fairly cumbersome, so letting the
foreach
statement provide syntactic sugar would be a nice simplification (and would amount to a generalization of the pairwiseLinq.Enumerable.Zip()
method):The above would yield:
and would be the equivalent of the (far more cumbersome) following:
Due to its resemblance to destructuring assignments, I think the purpose of the syntax is easy to infer.
Note:
With multiple iterator variables on the LHS (which activates the new feature being introduced), their number would be required to match the number of explicitly listed collections (RHS); e.g.:
foreach ($aElem, $bElem, $cElem in $a, $b, $c) { ... }
... VALID: 3 iterator variables, 3 collectionsforeach ($aElem, $bElem in $a, $b, $c) { ... }
... INVALID: only 2 iterator variables vs. 3 collectionsThe single-iterator variable (the currently supported syntax) would continue to function as-is; e.g.:
foreach ($elem in $a, $b, $c) { ... }
... RHS is conceived as a single collection to iterate over directly, resulting in 3 iterations with$elem
bound to the values of$a
,$b
,$c
in sequence (irrespective of whether these variables contain scalars or collections).The largest among the RHS collections would drive the number of iterations, and the iterator variables would contain
$null
for those collections that have run out of elements.Backward-compatibility considerations: N/A, because the proposed new syntax currently results in a syntax error.
The text was updated successfully, but these errors were encountered: