1

For example how would this be done using a variable to specify the sort field 'price' instead of being hard coded?

$XLM.Products.Product | sort {[decimal]$_.price} | % { [void]$XML.Products.AppendChild($_) }

Objective pseudo code

$XML_Node_Attribute_Name = 'price'
$XLM.Products.Product | sort {[decimal]$_.$XML_Node_Attribute_Name} | % { [void]$XML.Products.AppendChild($_) }

Needs to be Powershell versions 4 and 5 compatible.

NOYB
  • 625
  • 8
  • 14
  • `$XLM.Products.Product | sort $XML_Node_Attribute_Name`? – iRon Aug 05 '23 at 06:39
  • 1
    Btw, a calculated property as `$XLM.Products.Product | sort {[decimal]$_.$XML_Node_Attribute_Name}` should also work. Sure that it is `$XLM`, an not `$XML`? Please add a [mcve] (to the question) which what you expect and what you actually receive as a sorted list. – iRon Aug 05 '23 at 06:50
  • You could also try adding $() around the $XML_Node_Attribute_Name, like: `$XLM.Products.Product | sort {[decimal]$_.$($XML_Node_Attribute_Name)} | % { [void]$XML.Products.AppendChild($_) }` Im not sure if that is just an example command, and your not actually using it, but you would also likely get an error if your trying to AppendChild, for a child that is already in that node, that is assuming $XLM is a typo. – KG-DROID Aug 06 '23 at 09:07
  • @KG-DROID, typo aside, the code tries to rearrange the `` elements in sort order, which does work (using a node that's already in the XML DOM with `.AppendChild()` is only a problem if you try to use an _ancestral_ node). `$_.$XML_Node_Attribute_Name` should work just fine, and enclosure in `$(...)` wouldn't make a difference (`$(...)` is rarely called for outside of string interpolation; if the value of an expression (other than a straight variable reference) is to be used as a property name, `(...)` will do; e.g. `'foo'.('L' + 'ength')` – mklement0 Aug 06 '23 at 13:57

1 Answers1

0

Your pseudo could should work, as PowerShell indeed allows you to use variable references as property names.

A simplified example:

$propName = 'Year'
(Get-Date).$propName # -> e.g., 2023

(...), the grouping operator, even allows you to use (the return value of) expressions as property names:

# Array element as property name.
$propNames = 'Year', 'Day'
(Get-Date).($propNames[0]) # -> e.g., 2023

# Property value of another object as property name.
$propNameObj = [pscustomobject] @{ Name = 'Year' }
(Get-Date).($propNameObj.Name) # -> e.g., 2023

# Arbitrary expression as property name.
$i = 2
([pscustomobject] @{ P1 = 1; P2 = 2; P3 = 3}).('P' + $i) # -> 2


Here's a self-contained example in the context of your scenario:

# Sample input XML
[xml] $xml = '
<Products>
  <Product price="20.99">Product A</Product>
  <Product price="30.99">Product C</Product>
  <Product price="10.99">Product B</Product>
</Products>
'

# Sort the <Product> elements by their 'price' attribute.
$attribName = 'price'
$xml.Products.Product |
  # Access the 'price' property (XML attribute) via a variable value.
  Sort-Object { [decimal] $_.$attribName } | 
  # "Append" the elements in sort order to their parent element, 
  # which effectively *rearranges* in them sort order.
  ForEach-Object { $null = $xml.Products.AppendChild($_) } 

# Display the resulting document.
# Note: [System.Xml.Linq.XDocument] is solely used for simple
#       pretty-printing of the XML structure.
Add-Type -AssemblyName System.Xml.Linq
([System.Xml.Linq.XDocument] $xml.OuterXml).ToString()

Note:

  • Even though it is technically an XML attribute that is sorted by, PowerShell surfaces it as a property, which it equally does for child elements - see this answer for details on this adaptation of the XML DOM ([xml] (System.Xml.XmlDocument) that PowerShell automatically provides.

The above outputs the following, showing the <Product> elements correctly sorted by price:

<Products>
  <Product price="10.99">Product B</Product>
  <Product price="20.99">Product A</Product>
  <Product price="30.99">Product C</Product>
</Products>
mklement0
  • 382,024
  • 64
  • 607
  • 775