Kavita/API/Comparators/NaturalSortComparer.cs
Joseph Milazzo 28688ada8e
In-Depth Filtering (#850)
* Laying the foundation for the filter rework

* Filtering by Genre is now possible.

* Cleaned up code and preparing for People filtering

* People filtering is hooked up for the frontend

* Filtering now works. On Deck does not work with filtering currently due to a unique implementation.

* More cleanup

* Implemented the ability to reset the filters

* Added a mobile drawer for filtering

* Added some additional cases for NaturalSortComparer. Filter now uses a drawer on smaller screens.

* Fixed a bug where backup service was not pointing to the correct directory.

* Undid the fix, it's working as expected
2021-12-15 08:23:10 -08:00

112 lines
2.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using static System.GC;
using static System.String;
namespace API.Comparators
{
/// <summary>
/// Attempts to emulate Windows explorer sorting
/// </summary>
/// <remarks>This is not thread-safe</remarks>
public sealed class NaturalSortComparer : IComparer<string>, IDisposable
{
private readonly bool _isAscending;
private Dictionary<string, string[]> _table = new();
private bool _disposed;
public NaturalSortComparer(bool inAscendingOrder = true)
{
_isAscending = inAscendingOrder;
}
int IComparer<string>.Compare(string x, string y)
{
if (x == y) return 0;
if (x != null && y == null) return -1;
if (x == null) return 1;
if (!_table.TryGetValue(x ?? Empty, out var x1))
{
x1 = Regex.Split(x ?? Empty, "([0-9]+)");
_table.Add(x ?? Empty, x1);
}
if (!_table.TryGetValue(y ?? Empty, out var y1))
{
y1 = Regex.Split(y ?? Empty, "([0-9]+)");
_table.Add(y ?? Empty, y1);
}
int returnVal;
for (var i = 0; i < x1.Length && i < y1.Length; i++)
{
if (x1[i] == y1[i]) continue;
if (x1[i] == Empty || y1[i] == Empty) continue;
returnVal = PartCompare(x1[i], y1[i]);
return _isAscending ? returnVal : -returnVal;
}
if (y1.Length > x1.Length)
{
returnVal = -1;
}
else if (x1.Length > y1.Length)
{
returnVal = 1;
}
else
{
returnVal = 0;
}
return _isAscending ? returnVal : -returnVal;
}
private static int PartCompare(string left, string right)
{
if (!int.TryParse(left, out var x))
return Compare(left, right, StringComparison.Ordinal);
if (!int.TryParse(right, out var y))
return Compare(left, right, StringComparison.Ordinal);
return x.CompareTo(y);
}
private void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// called via myClass.Dispose().
_table.Clear();
_table = null;
}
// Release unmanaged resources.
// Set large fields to null.
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
SuppressFinalize(this);
}
~NaturalSortComparer() // the finalizer
{
Dispose(false);
}
}
}