Я бы хотел реализовать различие между List / Array в C#, но я столкнулся с проблемой. Вот что у меня есть:
public struct ListDiff
{
public IReadOnlyList<int> Insertions { get; }
public IReadOnlyList<int> Deletions { get; }
public IReadOnlyList<(int source, int destination)> Moves { get; }
ListDiff(IReadOnlyList<int> insertions, IReadOnlyList<int> deletions, IReadOnlyList<(int, int)> moves)
{
Insertions = insertions;
Deletions = deletions;
Moves = moves;
}
static IReadOnlyDictionary<T, int> ToDict<T>(IReadOnlyList<T> values)
{
var dict = new Dictionary<T, int>();
for (int i = 0; i < values.Count(); ++i)
{
dict[values[i]] = i;
}
return dict;
}
// Assuming a T with a meaningful implementation of GetHashCode()
public static ListDiff GetDiff<T>(IReadOnlyList<T> previous, IReadOnlyList<T> current)
{
// map into Dictionary<T, int> for fast "contains member"-lookup
var previousDict = ToDict(previous);
var currentDict = ToDict(current);
var deletions = new List<int>();
var insertions = new List<int>();
var moves = new List<(int source, int destination)>();
foreach (var (item, index) in previousDict)
{
if (currentDict.ContainsKey(item))
{
if (currentDict[item] != index)
{
// item is in both and its index has changed == it moved
moves.Add((index, currentDict[item]));
}
}
else
{
// item was in previous, not in current
deletions.Add(index);
}
}
foreach (var (item, index) in currentDict)
{
if (!previousDict.ContainsKey(item))
{
// item is in current, not in previous
insertions.Add(index);
}
}
return new ListDiff(insertions, deletions, moves);
}
}
Существует три типа «действий»: insertions
, deletions
и moves
. Все это прекрасно работает - если не супер эффективно - для идентификации элементов, которые были вставлены и удалены. Проблема в том, что любые элементы, которые были «перемещены» в результате вставок или удалений, возвращаются как moves
: то есть, если myItem
было в старом Списке с индексом 1, а новый Список удален с 0, myItem
считается как движение от 1-> 0, даже если его движение было строго результатом других действий.
Для моих целей мне нужно игнорировать шаги, которые были строго результатом других действий. Какой алгоритм я могу использовать, чтобы «отменить» избыточные ходы?
Или есть пропущенный мной библиотечный / встроенный метод, который делает это более эффективно, чем я когда-либо мог мечтать?
Общие примечания:
- В настоящее время я работаю со списками максимум из 3 элементов, но хотел бы применить этот процесс к спискам из потенциально около 100 элементов
- ожидание асинхронного c полу-продолжительного процесса приемлемо при необходимости
- Решение, которое также учитывает "обновленные" Ts, идеально, но не обязательно