Reputation: 22404
I was looking for a "window" function like F#'s Seq.windowed
or the Reactive extensions Window
. It looks like it would be provided by the likes of Select-Object
(which already has take / skip functionality), but it is not.
If nothing is readily available, any ideas on implementing "window" without unnecessary procedural looping?
I'm looking for something that works with the PowerShell pipeline nicely.
Upvotes: 3
Views: 305
Reputation: 22132
You need to use some kind of queue to accumulate and rotate enough previous pipeline items:
function Window {
param($Size)
begin {
$Queue = [Collections.Queue]::new($Size)
}
process {
$Queue.Enqueue($_)
if($Queue.Count -eq $Size) {
@(
,$Queue.ToArray()
[void]$Queue.Dequeue()
)
}
}
}
And you can use it like this:
1..10 | Window 4 | Window 3 | Format-Custom -Expand CoreOnly
Upvotes: 3
Reputation: 47842
There is no such built-in as far as I know. PowerShell's handling of the pipeline and in particular its strong insistence on unrolling enumerables makes it a bit difficult to be able to just write a function to pipe into, but as a general expression:
$a = 1..10 # an array contain 1 through 10
$w = 4 # window size of 4
0..($a.Length-$w) | ForEach-Object -Process {
$a[$_..($_+$w-1)]
}
That does process arrays that are the window you want. It's easier to see like this:
0..($a.Length-$w) | ForEach-Object -Process {
"This is my window: $($a[$_..($_+$w-1)])"
}
The thing is, if you tried to do the above this way:
0..($a.Length-$w) | ForEach-Object -Process {
$a[$_..($_+$w-1)]
} | ForEach-Object -Process {
"This is my window: $_"
}
You'd be very disappointed as the "window" array was unrolled before being piped into ForEach-Object
; a problem you'd have with any pipeline function you wrote to handle this as well.
There's no way to compensate for it within the function, you'd have to take care to use it inside the ForEach-Object
block because even on assignment:
$myWindows = 0..($a.Length-$w) | ForEach-Object -Process {
$a[$_..($_+$w-1)]
}
It would be unrolled.
So then you would need something like:
0..($a.Length-$w) | ForEach-Object -Begin {
$myWindows = @()
} -Process {
$myWindows += @(,$a[$_..($_+$w-1)])
}
Which you could then use like:
$myWindows | ForEach-Object -Process {
"This is my window: $_"
}
Upvotes: 1