This is and example of the XML for Pioneer Rekordbox DJ Software.
- Using Python, I am trying to re-sort the XML child tags by the START attribute for each POSITION_MARK tag. The POSITION MARK tag basically represent cue points for the DJ software.
I looked at this Stackoverflow question, but they only sort by whole numbers, not floats:
Python sort XML elements by and tag and attributes recursively
But I need to sort by the float value in each attribute.
- Each TRACK tag has POSITION_MARK child tags with a Start attribute float value.
- How do you re-sort the POSITION_MARK child tags by there float number in the Start attribute.
- This way when it rewrites the XML, each TRACK tags POSITION_MARK children are sorted in ascending order (smallest to largest) by their Start attribute value which is a float value.
How do you sort in Python using xmltree sorted to re-sort the POSITION_MARK children by their START attribute that contain float values?
<?xml version="1.0" ?>
<DJ_PLAYLISTS>
<COLLECTION>
<TRACK>
<POSITION_MARK Start="22.093" />
<POSITION_MARK Start="44.162" />
<POSITION_MARK Start="88.300" />
<POSITION_MARK Start="110.369" />
<POSITION_MARK Start="132.438" />
<POSITION_MARK Start="11.059" />
<POSITION_MARK Start="220.714" />
<POSITION_MARK Start="242.783" />
</TRACK>
<TRACK>
<POSITION_MARM Start="0.024" />
<POSITION_MARK Start="30.024" />
<POSITION_MARK Start="60.024" />
<POSITION_MARK Start="90.024" />
<POSITION_MARK Start="120.024" />
<POSITION_MARK Start="150.024" />
<POSITION_MARK Start="180.024" />
</TRACK>
</COLLECTION>
</DJ_PLAYLISTS>
>Solution :
xml.etree.ElementTree.Element objets are designed to look / feel like lists. Sadly they are not actually lists, and don’t support list.sort, however if you slice them you do get a list of sub-elements in the range. So given an element e, e[:] will return a list of all its children elements.
You can then sort that in-place, then use slice-setting (e[:] = ...) to replace the existing sequence of children by the now-sorted list.
doc = ET.fromstring(doc)
# iterate all the TRACK elements
for track in doc.iterfind(".//TRACK"):
# get a list of the track's children (POSITION_MARK)
children = track[:]
# sort in place, using the `Start` attribute interpreted as a float (by actually parsing it to a float)
children.sort(key=lambda e: float(e.get('Start')))
# set the now-sorted children back on
track[:] = children
print(ET.tostring(doc, encoding='unicode'))
However, sorted takes an iterable, which an Element is, so you can use that to get-and-sort the sub-elements in a single operation. And since it also returns a list, you can then directly set the result back as above:
doc = ET.fromstring(doc)
for track in doc.iterfind(".//TRACK"):
track[:] = sorted(track, key=lambda e: float(e.get('Start')))
print(ET.tostring(doc, encoding='unicode'))