I have been attempting to dynamically create a list in a .xml file based on a list of all the files in a folder. However, the list in the .xml file needs to be in a certain order and have various information with it.
I have successfully made a list, I just haven't figured out how to order it the way that I want.
Example: List of files in the D:\ folder
BG.pdf
CS.pdf
EN.pdf
17.pdf
This folder can have any number of PDF files in it and the list created needs to consistently have the same/similar order. Essentially 17 needs to be first and EN needs to be second, everything else can be put in alphabetical order.
Here is what I have so far:
$filePath = "D:\"
$list = Get-ChildItem -Path $filePath -Recurse | `
Where-Object { $_.PSIsContainer -eq $false -and $_.Extension -ne '.srt' }
New-Item $filePath’\.PDFList.txt'
$var = "<PDFList>" | Out-File -Append $filePath’\.PDFList.txt'
ForEach($n in $list){
if($n.name.Contains('EN')) {
$var = " <PDF><language>English</language><filename>" $n.Name "</filename></PDF>" | Out-File -Append $filePath’\.PDFList.txt'
} elseif($n.name.Contains('BG')) {
$var = " <PDF><language>Bulgarian</language><filename>" $n.Name "</filename></PDF>" | Out-File -Append $filePath’\.PDFList.txt'
} elseif($n.name.Contains('CS')) {
$var = " <PDF><language>Czech</language><filename>" $n.Name "</filename></PDF>" | Out-File -Append $filePath’\.PDFList.txt'
} elseif($n.name.Contains('17')) {
$var = " <PDF><language>17</language><filename>" $n.Name "</filename></PDF>" | Out-File -Append $filePath’\.PDFList.txt'
}
$var = "<PDFList>" | Out-File -Append $filePath’\.PDFList.txt'
Rename-Item -Path $filePath’\.PDFList.txt' -NewName $filePath’\.PDFList.xml'
This is what the current result is:
<PDFList>
<PDF><language>Bulgarian</language><filename>BG.pdf</filename></PDF>
<PDF><language>Czech</language><filename>CS.pdf</filename></PDF>
<PDF><language>English</language><filename>EN.PDF</filename></PDF>
<PDF><language>17</language><filename>17.pdf</filename></PDF>
<PDFList>
I want the list put in the .xml file to be this:
<PDFList>
<PDF><language>17</language><filename>17.pdf</filename></PDF>
<PDF><language>English</language><filename>EN.PDF</filename></PDF>
<PDF><language>Bulgarian</language><filename>BG.pdf</filename></PDF>
<PDF><language>Czech</language><filename>CS.pdf</filename></PDF>
<PDFList>
CodePudding user response:
You could also use the Array IndexOf() method combined with Sort-Object
for this:
$sorted = 'GB','CS','EN','17' | Sort-Object { ('17','EN','GB','CS').IndexOf($_) }
$sorted
17
EN
GB
CS
CodePudding user response:
Use PowerShell's ability to define custom .NET classes to implement a custom IComparer[string]
, which you can then use with methods like Array.Sort()
and List.Sort()
:
# Define our custom string comparer
class SevenTeenAndEnglishComparer : System.Collections.Generic.IComparer[string]
{
[int]
Compare([string]$a, [string]$b){
# Handle 17* first
if($a -like '17*' -and $b -notlike '17*'){
return -1
}
elseif($b -like '17*' -and $a -notlike '17*'){
return 1
}
# Handle EN* next
if($a -like 'EN*' -and $b -notlike 'EN*'){
return -1
}
elseif($b -like 'EN*' -and $a -notlike 'EN*'){
return 1
}
# Handle everything else as we normally would
return [System.StringComparer]::CurrentCultureIgnoreCase.Compare($a,$b)
}
}
# Prepare string array as input for `Array.Sort()`
$strings = -split @'
BG.pdf
CS.pdf
EN.pdf
17.pdf
'@ -as [string[]]
# Sort strings, but do it according to the logic of our new custom comparer
[array]::Sort($strings, [SevenTeenAndEnglishComparer]::new())
# Observe that strings are now ordered correctly
$strings
CodePudding user response:
A pragmatic solution is to use two sort criteria with Sort-Object
:
# Simulate the output from your Get-ChildItem call
$list = [System.IO.FileInfo[]] (
'CS.pdf',
'BG.pdf',
'EN.pdf',
'17.pdf'
)
$customSortedNames = (
$list | Sort-Object { $_.BaseName -notin '17', 'EN' }, BaseName
).Name
# Output the sort result
$customSortedNames
The above yields:
17.pdf
EN.pdf
BG.pdf
CS.pdf
Note that this relatively simple solution relies on the fact that the desired custom order of the two exceptions - 17
first, then EN
- happens to reflect the lexical sort order among them too.
With a fixed number of known exceptions with known relative sort order, you could also do something like (note that .IndexOf()
is case-sensitive; for a case-insensitive alternative, see this answer):
# Note that the desired order among the exceptions must be stated *in reverse*.
$list | Sort-Object { -('EN', '17').IndexOf($_.BaseName) }, BaseName
If you need more sophisticated logic than that, consider Mathias R. Jessen's helpful answer.