Source/Public/Get-ChildNode.ps1
Using NameSpace System.Management.Automation.Language <# .SYNOPSIS Gets the child nodes of an object-graph .DESCRIPTION Gets the (unique) nodes and child nodes in one or more specified locations of an object-graph The returned nodes are unique even if the provide list of input parent nodes have an overlap. .EXAMPLE # Select all leaf nodes in a object graph Given the following object graph: $Object = @{ Comment = 'Sample ObjectGraph' Data = @( @{ Index = 1 Name = 'One' Comment = 'First item' } @{ Index = 2 Name = 'Two' Comment = 'Second item' } @{ Index = 3 Name = 'Three' Comment = 'Third item' } ) } The following example will receive all leaf nodes: $Object | Get-ChildNode -Recurse -Leaf PathName Name Depth Value -------- ---- ----- ----- .Data[0].Comment Comment 3 First item .Data[0].Name Name 3 One .Data[0].Index Index 3 1 .Data[1].Comment Comment 3 Second item .Data[1].Name Name 3 Two .Data[1].Index Index 3 2 .Data[2].Comment Comment 3 Third item .Data[2].Name Name 3 Three .Data[2].Index Index 3 3 .Comment Comment 1 Sample ObjectGraph .EXAMPLE # update a property The following example selects all child nodes named `Comment` at a depth of `3`. Than filters the one that has an `Index` sibling with the value `2` and eventually sets the value (of the `Comment` node) to: 'Two to the Loo'. $Object | Get-ChildNode -AtDepth 3 -Include Comment | Where-Object { $_.ParentNode.GetChildNode('Index').Value -eq 2 } | ForEach-Object { $_.Value = 'Two to the Loo' } ConvertTo-Expression $Object @{ Data = @{ Comment = 'First item' Name = 'One' Index = 1 }, @{ Comment = 'Two to the Loo' Name = 'Two' Index = 2 }, @{ Comment = 'Third item' Name = 'Three' Index = 3 } Comment = 'Sample ObjectGraph' } See the [PowerShell Object Parser][1] For details on the `[PSNode]` properties and methods. .PARAMETER InputObject The concerned object graph or node. .PARAMETER Recurse Recursively iterates through all embedded property objects (nodes) to get the selected nodes. The maximum depth of of a specific node that might be retrieved is define by the `MaxDepth` of the (root) node. To change the maximum depth the (root) node needs to be loaded first, e.g.: Get-Node <InputObject> -Depth 20 | Get-ChildNode ... (See also: [`Get-Node`][2]) > [!NOTE] > If the [AtDepth] parameter is supplied, the object graph is recursively searched anyways > for the selected nodes up till the deepest given `AtDepth` value. .PARAMETER AtDepth When defined, only returns nodes at the given depth(s). > [!NOTE] > The nodes below the `MaxDepth` can not be retrieved. .PARAMETER ListChild Returns the closest nodes derived from a **list node**. .PARAMETER Include Returns only nodes derived from a **map node** including only the ones specified by one or more string patterns defined by this parameter. Wildcard characters are permitted. > [!NOTE] > The [-Include] and [-Exclude] parameters can be used together. However, the exclusions are applied > after the inclusions, which can affect the final output. .PARAMETER Exclude Returns only nodes derived from a **map node** excluding the ones specified by one or more string patterns defined by this parameter. Wildcard characters are permitted. > [!NOTE] > The [-Include] and [-Exclude] parameters can be used together. However, the exclusions are applied > after the inclusions, which can affect the final output. .PARAMETER Literal The values of the [-Include] - and [-Exclude] parameters are used exactly as it is typed. No characters are interpreted as wildcards. .PARAMETER Leaf Only return leaf nodes. Leaf nodes are nodes at the end of a branch and do not have any child nodes. You can use the [-Recurse] parameter with the [-Leaf] parameter. .PARAMETER IncludeSelf Includes the current node with the returned child nodes. .LINK [1]: https://github.com/iRon7/ObjectGraphTools/blob/main/Docs/ObjectParser.md "PowerShell Object Parser" [2]: https://github.com/iRon7/ObjectGraphTools/blob/main/Docs/Get-Node.md "Get-Node" #> function Get-ChildNode { [OutputType([PSNode[]])] [CmdletBinding(DefaultParameterSetName='ListChild')] param( [Parameter(Mandatory = $true, ValueFromPipeLine = $true)] $InputObject, [switch] $Recurse, [ValidateRange(0, [int]::MaxValue)] [int[]] $AtDepth, [Parameter(ParameterSetName='ListChild')] [switch] $ListChild, [Parameter(ParameterSetName='MapChild', Position = 0)] [string[]] $Include, [Parameter(ParameterSetName='MapChild')] [string[]] $Exclude, [Parameter(ParameterSetName='MapChild')] [switch] $Literal, [switch] $Leaf, [Alias('Self')][switch] $IncludeSelf ) begin { $UniqueNodes = [System.Collections.Generic.Dictionary[Int, System.Collections.Generic.HashSet[String]]]::new() $SearchDepth = if ($PSBoundParameters.ContainsKey('AtDepth')) { [System.Linq.Enumerable]::Max($AtDepth) - $Node.Depth - 1 } elseif ($Recurse) { -1 } else { 0 } $NodeOrigin = if ($ListChild) { [PSNodeOrigin]'List' } elseif ($Null -ne $Include -or $Null -ne $Exclude) { [PSNodeOrigin]'Map' } else { [PSNodeOrigin]0 } } process { if ($InputObject -is [PSNode]) { $Self = $InputObject } else { $Self = [PSNode]::ParseInput($InputObject, $MaxDepth) } $ValueHash = $Self.RootNode.Value.GetHashCode() if (-Not $UniqueNodes.ContainsKey($ValueHash)) { $UniqueNodes[$ValueHash] = [System.Collections.Generic.HashSet[String]]::new() # Note: this HashSet is case sensitive (as any -nested- dictionary might be) } $NodeList = [System.Collections.Generic.List[PSNode]]::new() if ($IncludeSelf) { $NodeList.Add($Self) } if ($Self -is [PSLeafNode]) { Write-Warning "The node '$($Self.PathName)' is a leaf node which does not contain any child nodes." } else { $Self.CollectChildNodes($NodeList, $SearchDepth, $NodeOrigin, $Leaf) } foreach ($Node in $NodeList) { if ( ( -not $PSBoundParameters.ContainsKey('AtDepth') -or $Node.Depth -in $AtDepth ) -and ( -not $Include -or ( ($Literal -and $Node.Name -in $Include) -or (-not $Literal -and $Include.where({ $Node.Name -like $_ }, 'first')) ) ) -and -not ( $Exclude -and ( ($Literal -and $Node.Name -in $Exclude) -or (-not $Literal -and $Exclude.where({ $Node.Name -like $_ }, 'first')) ) ) ) { if ($UniqueNodes[$ValueHash].Add($Node.PathName)) { if ($ValueOnly) { $Node.Value } else { $Node } } } } } } |