Add RigidMultiPredicateSort to replace some .ThenBy() boilerplate
As documented, the class "Sorts using a single primary predicate, with subsorts using the remaining predicates in order." So only the most recent column-header-click is taken into account. I've got a WIP class in #if false for providing the "remember which column headers I clicked and in which order" behaviour, but it doesn't look like that behaviour actually exists in the codebase?
This commit is contained in:
parent
2dc28ecc4c
commit
33d8f4a62c
|
@ -6,7 +6,7 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
using BizHawk.Common.CollectionExtensions;
|
using BizHawk.Common;
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
namespace BizHawk.Client.Common
|
namespace BizHawk.Client.Common
|
||||||
|
@ -414,51 +414,22 @@ namespace BizHawk.Client.Common
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Sort(string column, bool reverse)
|
private static readonly RigidMultiPredicateSort<Cheat> ColumnSorts
|
||||||
|
= new RigidMultiPredicateSort<Cheat>(new Dictionary<string, Func<Cheat, IComparable>>
|
||||||
{
|
{
|
||||||
_cheatList = column switch
|
[NameColumn] = c => c.Name,
|
||||||
{
|
[AddressColumn] = c => c.Address ?? 0L,
|
||||||
NameColumn => _cheatList.OrderBy(c => c.Name, reverse)
|
[ValueColumn] = c => c.Value ?? 0,
|
||||||
.ThenBy(c => c.Address ?? 0)
|
[CompareColumn] = c => c.Compare ?? 0,
|
||||||
.ToList(),
|
[OnColumn] = c => c.Enabled,
|
||||||
AddressColumn => _cheatList.OrderBy(c => c.Address ?? 0, reverse)
|
[DomainColumn] = c => c.Domain.Name,
|
||||||
.ThenBy(c => c.Name)
|
[SizeColumn] = c => (int) c.Size,
|
||||||
.ToList(),
|
[EndianColumn] = c => c.BigEndian,
|
||||||
ValueColumn => _cheatList.OrderBy(c => c.Value ?? 0, reverse)
|
[TypeColumn] = c => c.Type,
|
||||||
.ThenBy(c => c.Name)
|
[ComparisonType] = c => c.ComparisonType
|
||||||
.ThenBy(c => c.Address ?? 0)
|
});
|
||||||
.ToList(),
|
|
||||||
CompareColumn => _cheatList.OrderBy(c => c.Compare ?? 0, reverse)
|
public void Sort(string column, bool reverse) => _cheatList = ColumnSorts.AppliedTo(_cheatList, column, firstIsDesc: reverse);
|
||||||
.ThenBy(c => c.Name)
|
|
||||||
.ThenBy(c => c.Address ?? 0)
|
|
||||||
.ToList(),
|
|
||||||
OnColumn => _cheatList.OrderBy(c => c.Enabled, reverse)
|
|
||||||
.ThenBy(c => c.Name)
|
|
||||||
.ThenBy(c => c.Address ?? 0)
|
|
||||||
.ToList(),
|
|
||||||
DomainColumn => _cheatList.OrderBy(c => c.Domain, reverse)
|
|
||||||
.ThenBy(c => c.Name)
|
|
||||||
.ThenBy(c => c.Address ?? 0)
|
|
||||||
.ToList(),
|
|
||||||
SizeColumn => _cheatList.OrderBy(c => (int) c.Size, reverse)
|
|
||||||
.ThenBy(c => c.Name)
|
|
||||||
.ThenBy(c => c.Address ?? 0)
|
|
||||||
.ToList(),
|
|
||||||
EndianColumn => _cheatList.OrderBy(c => c.BigEndian, reverse)
|
|
||||||
.ThenBy(c => c.Name)
|
|
||||||
.ThenBy(c => c.Address ?? 0)
|
|
||||||
.ToList(),
|
|
||||||
TypeColumn => _cheatList.OrderBy(c => c.Type, reverse)
|
|
||||||
.ThenBy(c => c.Name)
|
|
||||||
.ThenBy(c => c.Address ?? 0)
|
|
||||||
.ToList(),
|
|
||||||
ComparisonType => _cheatList.OrderBy(c => c.ComparisonType, reverse)
|
|
||||||
.ThenBy(c => c.Name)
|
|
||||||
.ThenBy(c => c.Address ?? 0)
|
|
||||||
.ToList(),
|
|
||||||
_ => _cheatList
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDefaultFileName(string defaultFileName)
|
public void SetDefaultFileName(string defaultFileName)
|
||||||
{
|
{
|
||||||
|
|
|
@ -342,41 +342,19 @@ namespace BizHawk.Client.EmuHawk
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly RigidMultiPredicateSort<IMovie> ColumnSorts
|
||||||
|
= new RigidMultiPredicateSort<IMovie>(new Dictionary<string, Func<IMovie, IComparable>>
|
||||||
|
{
|
||||||
|
["File"] = x => Path.GetFileName(x.Filename),
|
||||||
|
["SysID"] = x => x.SystemID,
|
||||||
|
["Game"] = x => x.GameName,
|
||||||
|
["Length (est.)"] = x => x.FrameCount
|
||||||
|
});
|
||||||
|
|
||||||
private void MovieView_ColumnClick(object sender, ColumnClickEventArgs e)
|
private void MovieView_ColumnClick(object sender, ColumnClickEventArgs e)
|
||||||
{
|
{
|
||||||
var columnName = MovieView.Columns[e.Column].Text;
|
var columnName = MovieView.Columns[e.Column].Text;
|
||||||
switch (columnName)
|
_movieList = ColumnSorts.AppliedTo(_movieList, columnName);
|
||||||
{
|
|
||||||
case "File":
|
|
||||||
default:
|
|
||||||
_movieList = _movieList.OrderBy(x => Path.GetFileName(x.Filename))
|
|
||||||
.ThenBy(x => x.SystemID)
|
|
||||||
.ThenBy(x => x.GameName)
|
|
||||||
.ThenBy(x => x.FrameCount)
|
|
||||||
.ToList();
|
|
||||||
break;
|
|
||||||
case "SysID":
|
|
||||||
_movieList = _movieList.OrderBy(x => x.SystemID)
|
|
||||||
.ThenBy(x => Path.GetFileName(x.Filename))
|
|
||||||
.ThenBy(x => x.GameName)
|
|
||||||
.ThenBy(x => x.FrameCount)
|
|
||||||
.ToList();
|
|
||||||
break;
|
|
||||||
case "Game":
|
|
||||||
_movieList = _movieList.OrderBy(x => x.GameName)
|
|
||||||
.ThenBy(x => Path.GetFileName(x.Filename))
|
|
||||||
.ThenBy(x => x.SystemID)
|
|
||||||
.ThenBy(x => x.FrameCount)
|
|
||||||
.ToList();
|
|
||||||
break;
|
|
||||||
case "Length (est.)":
|
|
||||||
_movieList = _movieList.OrderBy(x => x.FrameCount)
|
|
||||||
.ThenBy(x => Path.GetFileName(x.Filename))
|
|
||||||
.ThenBy(x => x.SystemID)
|
|
||||||
.ThenBy(x => x.GameName)
|
|
||||||
.ToList();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (_sortedCol == columnName && _sortReverse)
|
if (_sortedCol == columnName && _sortReverse)
|
||||||
{
|
{
|
||||||
_movieList.Reverse();
|
_movieList.Reverse();
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace BizHawk.Common
|
||||||
|
{
|
||||||
|
#if false
|
||||||
|
/// <summary>Sorts using a reorderable list of predicates.</summary>
|
||||||
|
/// <seealso cref="RigidMultiPredicateSort{T}"/>
|
||||||
|
public sealed class MultiPredicateSort<T>
|
||||||
|
{
|
||||||
|
private readonly int _count;
|
||||||
|
|
||||||
|
/// <remarks>TODO would an array be faster?</remarks>
|
||||||
|
private readonly List<(string ID, bool IsDesc)> _order;
|
||||||
|
|
||||||
|
private readonly IReadOnlyDictionary<string, Func<T, IComparable>> _predicates;
|
||||||
|
|
||||||
|
public MultiPredicateSort(IReadOnlyDictionary<string, Func<T, IComparable>> predicates)
|
||||||
|
{
|
||||||
|
_count = predicates.Count;
|
||||||
|
if (_count == 0) throw new ArgumentException("must have at least 1 predicate", nameof(predicates));
|
||||||
|
_order = predicates.Select(kvp => (kvp.Key, false)).ToList();
|
||||||
|
_predicates = predicates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> AppliedTo(IReadOnlyCollection<T> list)
|
||||||
|
{
|
||||||
|
var temp = _order[0].IsDesc
|
||||||
|
? list.OrderByDescending(_predicates[_order[0].ID])
|
||||||
|
: list.OrderBy(_predicates[_order[0].ID]);
|
||||||
|
for (var i = 1; i < _count; i++)
|
||||||
|
{
|
||||||
|
temp = _order[i].IsDesc
|
||||||
|
? temp.ThenByDescending(_predicates[_order[i].ID])
|
||||||
|
: temp.ThenBy(_predicates[_order[i].ID]);
|
||||||
|
}
|
||||||
|
return temp.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>Sorts using a single primary predicate, with subsorts using the remaining predicates in order.</summary>
|
||||||
|
#if false
|
||||||
|
/// <seealso cref="MultiPredicateSort{T}"/>
|
||||||
|
#endif
|
||||||
|
public sealed class RigidMultiPredicateSort<T>
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyDictionary<string, Func<T, IComparable>> _predicates;
|
||||||
|
|
||||||
|
public RigidMultiPredicateSort(IReadOnlyDictionary<string, Func<T, IComparable>> predicates)
|
||||||
|
{
|
||||||
|
if (predicates.Count == 0) throw new ArgumentException("must have at least 1 predicate", nameof(predicates));
|
||||||
|
_predicates = predicates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <remarks>sorts using <paramref name="idOfFirst"/> asc/desc (by <paramref name="firstIsDesc"/>), then by the remaining predicates, all asc</remarks>
|
||||||
|
public List<T> AppliedTo(IReadOnlyCollection<T> list, string idOfFirst, bool firstIsDesc = false)
|
||||||
|
{
|
||||||
|
var temp = firstIsDesc
|
||||||
|
? list.OrderByDescending(_predicates[idOfFirst])
|
||||||
|
: list.OrderBy(_predicates[idOfFirst]);
|
||||||
|
foreach (var kvp in _predicates)
|
||||||
|
{
|
||||||
|
if (kvp.Key == idOfFirst) continue;
|
||||||
|
temp = temp.ThenBy(kvp.Value);
|
||||||
|
}
|
||||||
|
return temp.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> AppliedTo(IReadOnlyCollection<T> list, string idOfFirst, IReadOnlyDictionary<string, bool> isDescMap)
|
||||||
|
{
|
||||||
|
var temp = isDescMap[idOfFirst]
|
||||||
|
? list.OrderByDescending(_predicates[idOfFirst])
|
||||||
|
: list.OrderBy(_predicates[idOfFirst]);
|
||||||
|
foreach (var kvp in _predicates)
|
||||||
|
{
|
||||||
|
if (kvp.Key == idOfFirst) continue;
|
||||||
|
temp = isDescMap[kvp.Key] ? temp.ThenByDescending(kvp.Value) : temp.ThenBy(kvp.Value);
|
||||||
|
}
|
||||||
|
return temp.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace BizHawk.Common.Tests.Common.MultiPredicateSort
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class MultiPredicateSortTests
|
||||||
|
{
|
||||||
|
private static readonly (int X, string Y)[] Unsorted = { (1, "b"), (2, "a"), (2, "b"), (1, "a") };
|
||||||
|
|
||||||
|
private static readonly (int X, string Y)[] SortedByXThenYDesc = { (1, "b"), (1, "a"), (2, "b"), (2, "a") };
|
||||||
|
|
||||||
|
private static readonly (int X, string Y)[] SortedByYDescThenXDesc = { (2, "b"), (1, "b"), (2, "a"), (1, "a") };
|
||||||
|
|
||||||
|
private static void AssertSequenceEqual<T>(IEnumerable<T> expected, IEnumerable<T> actual) => Assert.IsTrue(expected.SequenceEqual(actual));
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void SanityCheck()
|
||||||
|
{
|
||||||
|
AssertSequenceEqual(
|
||||||
|
SortedByYDescThenXDesc,
|
||||||
|
Unsorted.OrderByDescending(t => t.Y).ThenByDescending(t => t.X)
|
||||||
|
);
|
||||||
|
AssertSequenceEqual(
|
||||||
|
SortedByYDescThenXDesc,
|
||||||
|
Unsorted.OrderByDescending(t => t.X).OrderByDescending(t => t.Y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestRigidSort()
|
||||||
|
{
|
||||||
|
var sorts = new RigidMultiPredicateSort<(int X, string Y)>(new Dictionary<string, Func<(int X, string Y), IComparable>>
|
||||||
|
{
|
||||||
|
["by_x"] = t => t.X,
|
||||||
|
["by_y"] = t => t.Y
|
||||||
|
});
|
||||||
|
AssertSequenceEqual(
|
||||||
|
SortedByXThenYDesc,
|
||||||
|
sorts.AppliedTo(
|
||||||
|
Unsorted,
|
||||||
|
"by_x",
|
||||||
|
new Dictionary<string, bool> { ["by_x"] = false, ["by_y"] = true }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
AssertSequenceEqual(
|
||||||
|
SortedByYDescThenXDesc,
|
||||||
|
sorts.AppliedTo(
|
||||||
|
Unsorted,
|
||||||
|
"by_y",
|
||||||
|
new Dictionary<string, bool> { ["by_x"] = true, ["by_y"] = true }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue