mirror of
https://github.com/claunia/marechai.git
synced 2026-07-08 17:57:08 +00:00
Implement natural string sorting for software versions
This commit is contained in:
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Marechai.ApiClient.Models;
|
||||
using Marechai.App.Navigation;
|
||||
using Marechai.Data;
|
||||
using Marechai.App.Presentation.Views;
|
||||
using Marechai.App.Services;
|
||||
using Marechai.App.Services.Caching;
|
||||
@@ -182,6 +183,7 @@ public partial class SoftwareViewViewModel : ObservableObject, IRegionAware
|
||||
|
||||
// Load versions (without nested releases)
|
||||
List<SoftwareVersionDto> versions = await _browsingService.GetVersionsAsync(softwareId);
|
||||
versions.Sort((a, b) => NaturalStringComparer.Instance.Compare(a.VersionString, b.VersionString));
|
||||
|
||||
foreach(SoftwareVersionDto version in versions)
|
||||
{
|
||||
|
||||
75
Marechai.Data/NaturalStringComparer.cs
Normal file
75
Marechai.Data/NaturalStringComparer.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Marechai.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Compares strings using natural sort order, where numeric segments are compared by value rather than
|
||||
/// lexicographically. For example, "4.00.96" sorts before "4.00.111".
|
||||
/// </summary>
|
||||
public sealed class NaturalStringComparer : IComparer<string>
|
||||
{
|
||||
public static readonly NaturalStringComparer Instance = new();
|
||||
|
||||
public int Compare(string x, string y)
|
||||
{
|
||||
if(ReferenceEquals(x, y)) return 0;
|
||||
if(x is null) return -1;
|
||||
if(y is null) return 1;
|
||||
|
||||
int ix = 0, iy = 0;
|
||||
|
||||
while(ix < x.Length && iy < y.Length)
|
||||
{
|
||||
bool xIsDigit = char.IsDigit(x[ix]);
|
||||
bool yIsDigit = char.IsDigit(y[iy]);
|
||||
|
||||
if(xIsDigit && yIsDigit)
|
||||
{
|
||||
// Skip leading zeros and compare numeric segments by value
|
||||
int xStart = ix;
|
||||
int yStart = iy;
|
||||
|
||||
while(ix < x.Length && x[ix] == '0') ix++;
|
||||
while(iy < y.Length && y[iy] == '0') iy++;
|
||||
|
||||
int xNumStart = ix;
|
||||
int yNumStart = iy;
|
||||
|
||||
while(ix < x.Length && char.IsDigit(x[ix])) ix++;
|
||||
while(iy < y.Length && char.IsDigit(y[iy])) iy++;
|
||||
|
||||
int xLen = ix - xNumStart;
|
||||
int yLen = iy - yNumStart;
|
||||
|
||||
// Longer numeric segment is larger
|
||||
if(xLen != yLen) return xLen.CompareTo(yLen);
|
||||
|
||||
// Same length: compare digit by digit
|
||||
for(int i = 0; i < xLen; i++)
|
||||
{
|
||||
int cmp = x[xNumStart + i].CompareTo(y[yNumStart + i]);
|
||||
|
||||
if(cmp != 0) return cmp;
|
||||
}
|
||||
|
||||
// Values equal — more leading zeros means smaller (e.g., "007" < "7" is debatable, keep stable)
|
||||
int xLeadingZeros = xNumStart - xStart;
|
||||
int yLeadingZeros = yNumStart - yStart;
|
||||
|
||||
if(xLeadingZeros != yLeadingZeros) return xLeadingZeros.CompareTo(yLeadingZeros);
|
||||
}
|
||||
else
|
||||
{
|
||||
int cmp = char.ToUpperInvariant(x[ix]).CompareTo(char.ToUpperInvariant(y[iy]));
|
||||
|
||||
if(cmp != 0) return cmp;
|
||||
|
||||
ix++;
|
||||
iy++;
|
||||
}
|
||||
}
|
||||
|
||||
return x.Length.CompareTo(y.Length);
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Marechai.ApiClient.Models;
|
||||
using Marechai.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Marechai.Pages.Software;
|
||||
@@ -80,6 +81,7 @@ public partial class View
|
||||
|
||||
_companies = await Service.GetCompaniesAsync(Id);
|
||||
_versions = await Service.GetVersionsAsync(Id);
|
||||
_versions.Sort((a, b) => NaturalStringComparer.Instance.Compare(a.VersionString, b.VersionString));
|
||||
|
||||
// Load all non-compilation releases for this software (flat list)
|
||||
_releases = await Service.GetReleasesBySoftwareAsync(Id);
|
||||
|
||||
Reference in New Issue
Block a user