Алгоритм рассчета списков / массивов - PullRequest
1 голос
/ 10 апреля 2020

Я бы хотел реализовать различие между 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, идеально, но не обязательно
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...