Reputation: 29209
I have the following code
function Get($url) {
$x = Invoke-RestMethod $url
# ....
$result # return a json which some values (id, name, color) and a list
}
Get "http://...." |
% {
$id = $_.id
$url = "${$_.url}/xxx"
Get $ur |
% {
$name = $_.name
$url = $_.url
Get $url |
% {
$color = $_.color
$url = $_.url
Get $url | % {
# other layer omitted
}
}
}
}
The code looks bad. It seems the embedded layers should be resolved by monad. Is there a way to do it in PowerShell?
The following shows a pseudo F# code.
myMonad {
let! (id1, _, _, urls1, _ ) = Get ["http://..."]
let! (_, name2, _, urls2, _ ) = Get urls1
let! (_, _, color3, urls3, names) = Get urls2
// ....
printfn "%d %s %s %A" id1, name2, color3, names
}
Upvotes: 2
Views: 656
Reputation: 439727
PowerShell is clearly not a function language, though it has some functional traits and its many constructs and dynamic nature allow you to emulate functional features:
The code below defines a recurse-and-aggregate function named recurse
that can invoke your Get
function as follows in order to aggregate properties Id
, Name
, and Color
from recursive calls to Get
via each returned object's .URL
property:
recurse -Function Get <# target function #> `
-Argument http://example.org <# input URL #> `
-RecurseOn URL <# what result property to recurse on #> `
-PropertyNames Id, Name, Color <# what properties to aggregate successively,
in each iteration #>
recurse
source code with sample code:
# A fairly generic recurse-and-aggregate function that aggregates properties from
# result objects from recursive calls to a given function based on a single result property.
function recurse($Function, $Arguments, $RecurseOn, $PropertyNames, $outProperties = [ordered] @{ }) {
# Call the target function with the specified arguments.
$result = & $Function $Arguments
# Split into the names of the current and the remaining properties to aggregate.
$propName, $remainingPropNames = $PropertyNames
# Add the value of the current property of interest to the output hashtable, if present.
if ($null -ne $result.$propName) {
if ($outProperties.Contains($propName)) { # not the first value
if ($outProperties.$propName -is [array]) { # already an array -> "extend" the array.
$outProperties.$propName += $result.$propName
}
else { # not an array yet -> convert to array, with the previous value and the new one as the elements.
$outProperties.$propName = $outProperties.$propName, $result.$propName
}
}
else { # first value -> assign as-is
$outProperties.$propName = $result.$propName
}
}
if ($remainingPropNames) {
# Recurse on the value(s) of the property specfied with -RecurseOn
foreach ($newArgument in $result.$RecurseOn) {
$null = recurse -Function $function -Argument $newArgument -RecurseOn $RecurseOn -PropertyNames $remainingPropNames -outProperties $outProperties
}
}
# Return the aggregated properties.
$outProperties
}
# Sample input function:
# It makes a REST call to the given URL and returns the
# result object.
function Get($url) {
Write-Host "Get $url"
$id = 1
$name = ''
$color = ''
if ($url -eq 'http://example.org') {
$urls = 'http://example.org/1', 'http://example.org/2'
$id = 1
}
elseif ($url -match 'http://example.org/\d$') {
$urls = "$url/a", "$url/b"
$name = 'test'
}
elseif ($url -match 'http://example.org/\d/a') {
$urls = '...'
$name = 'test'
$color = "[color of $url] #1", "[color of $url] #2"
}
elseif ($url -match 'http://example.org/\d/b') {
$urls = '...'
$name = 'test'
$color = "[color of $url] #1", "[color of $url] #2"
}
[pscustomobject] @{
URL = $urls
Id = $id
Name = $name
Color = $color
}
}
# Call the recurse-and-aggregate function, passing it the name of the Get()
# function, an input URL, what result property to recurse on, and a list of properties to aggregate.
# The output is an ordered hashtable containing the aggregated property values.
recurse -Function Get <# target function #> `
-Argument http://example.org <# input URL #> `
-RecurseOn URL <# what result property to recurse on #> `
-PropertyNames Id, Name, Color <# what properties to aggregate successively,
in each iteration #>
The above yields:
Name Value
---- -----
Id 1
Name {test, test}
Color {[color of http://example.org/1/a] #1, [color of http://example.org/1/a] #2, [color of http://example.org/1/b] #1, [color of htt…
Upvotes: 1
Reputation: 27516
Well, you could insert F# code into Powershell with Add-Type. See Example 7: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-type?view=powershell-5.1
Upvotes: 0