Какой наименьший объем кода необходим для обновления одного списка другим списком? - PullRequest
2 голосов
/ 29 сентября 2008

Предположим, у меня есть один список:

IList<int> originalList = new List<int>();
originalList.add(1);
originalList.add(5);
originalList.add(10);

И еще один список ...

IList<int> newList = new List<int>();
newList.add(1);
newList.add(5);
newList.add(7);  
newList.add(11);

Как я могу обновить originalList, чтобы:

  1. Если int появляется в newList, оставьте
  2. Если int не отображается в newList, удалите
  3. Добавление любых целых из newList в originalList, которых там еще нет

Таким образом - создание содержимого originalList:

{ 1, 5, 7, 11 }

Причина, по которой я спрашиваю, заключается в том, что у меня есть объект с коллекцией детей. Когда пользователь обновляет эту коллекцию, вместо того, чтобы просто удалить все дочерние элементы, а затем вставить их выборки, я думаю, что было бы более эффективно, если бы я просто действовал на добавленных или удаленных дочерних элементах, вместо того, чтобы разрушать всю коллекцию и вставлять newList детей, как будто они все новые.

РЕДАКТИРОВАТЬ - Извините - я написал ужасный заголовок ... Я должен был написать «наименьшее количество кода» вместо «работоспособного». Я думаю, что это отбросило много ответов, которые я получил. Все они великолепны ... спасибо!

Ответы [ 10 ]

5 голосов
/ 29 сентября 2008
originalList = newList;

Или, если вы предпочитаете, чтобы они были отдельными списками:

originalList = new List<int>(newList);

Но, так или иначе, делает то, что ты хочешь. По вашим правилам после обновления originalList будет идентичен newList.

ОБНОВЛЕНИЕ: Я благодарю вас всех за поддержку этого ответа, но после более внимательного прочтения вопроса я считаю, что мой другой ответ (ниже) является правильным.

2 голосов
/ 29 сентября 2008

Если вы используете несколько методов расширения LINQ, вы можете сделать это в две строки:

originalList.RemoveAll(x => !newList.Contains(x));
originalList.AddRange(newList.Where(x => !originalList.Contains(x)));

Это предполагает (как и решения других людей), что вы переопределили Равные в вашем исходном объекте. Но если по какой-то причине вы не можете переопределить Equals, вы можете создать IEqualityOperator следующим образом:

class EqualThingTester : IEqualityComparer<Thing>
{
    public bool Equals(Thing x, Thing y)
    {
        return x.ParentID.Equals(y.ParentID);
    }

    public int GetHashCode(Thing obj)
    {
        return obj.ParentID.GetHashCode();
    }
}

Тогда вышеприведенные строки становятся:

originalList.RemoveAll(x => !newList.Contains(x, new EqualThingTester()));
originalList.AddRange(newList.Where(x => !originalList.Contains(x, new EqualThingTester())));

И если вы все равно передаете IEqualityOperator, вы можете сделать вторую строку еще короче:

originalList.RemoveAll(x => !newList.Contains(x, new EqualThingTester()));
originalList.AddRange(newList.Except(originalList, new EqualThingTester()));
1 голос
/ 29 сентября 2008

LINQ решение:

originalList = new List<int>(
                      from x in newList
                      join y in originalList on x equals y into z
                      from y in z.DefaultIfEmpty()
                      select x);
1 голос
/ 29 сентября 2008

Если вы не беспокоитесь о возможном заказе, Hashtable / HashSet, вероятно, будет самым быстрым.

1 голос
/ 29 сентября 2008

Извините, написал мой первый ответ, прежде чем я увидел ваш последний абзац.

for(int i = originalList.length-1; i >=0; --i)
{
     if (!newList.Contains(originalList[i])
            originalList.RemoveAt(i);
}

foreach(int n in newList)
{
     if (!originaList.Contains(n))
           originalList.Add(n);
}
0 голосов
/ 29 сентября 2008

, если вы используете .Net 3.5

var List3 = List1.Intersect(List2);

Создает новый список, который содержит пересечение двух списков, что, как я полагаю, вы стреляете здесь.

0 голосов
/ 29 сентября 2008

Это распространенная проблема, с которой сталкиваются разработчики при написании пользовательских интерфейсов для поддержания связей базы данных «многие ко многим». Я не знаю, насколько это эффективно, но я написал вспомогательный класс для обработки этого сценария:

public class IEnumerableDiff<T>
{
    private delegate bool Compare(T x, T y);

    private List<T> _inXAndY;
    private List<T> _inXNotY;
    private List<T> _InYNotX;

    /// <summary>
    /// Compare two IEnumerables.
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="compareKeys">True to compare objects by their keys using Data.GetObjectKey(); false to use object.Equals comparison.</param>
    public IEnumerableDiff(IEnumerable<T> x, IEnumerable<T> y, bool compareKeys)
    {
        _inXAndY = new List<T>();
        _inXNotY = new List<T>();
        _InYNotX = new List<T>();
        Compare comparer = null;
        bool hit = false;

        if (compareKeys)
        {
            comparer = CompareKeyEquality;
        }
        else
        {
            comparer = CompareObjectEquality;
        }


        foreach (T xItem in x)
        {
            hit = false;
            foreach (T yItem in y)
            {
                if (comparer(xItem, yItem))
                {
                    _inXAndY.Add(xItem);
                    hit = true;
                    break;
                }
            }
            if (!hit)
            {
                _inXNotY.Add(xItem);
            }
        }

        foreach (T yItem in y)
        {
            hit = false;
            foreach (T xItem in x)
            {
                if (comparer(yItem, xItem))
                {
                    hit = true;
                    break;
                }
            }
            if (!hit)
            {
                _InYNotX.Add(yItem);
            }
        }
    }

    /// <summary>
    /// Adds and removes items from the x (current) list so that the contents match the y (new) list.
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="compareKeys"></param>
    public static void SyncXList(IList<T> x, IList<T> y, bool compareKeys)
    {
        var diff = new IEnumerableDiff<T>(x, y, compareKeys);
        foreach (T item in diff.InXNotY)
        {
            x.Remove(item);
        }
        foreach (T item in diff.InYNotX)
        {
            x.Add(item);
        }
    }

    public IList<T> InXAndY
    {
        get { return _inXAndY; }
    }

    public IList<T> InXNotY
    {
        get { return _inXNotY; }
    }

    public IList<T> InYNotX
    {
        get { return _InYNotX; }
    }

    public bool ContainSameItems
    {
        get { return _inXNotY.Count == 0 && _InYNotX.Count == 0; }
    }

    private bool CompareObjectEquality(T x, T y)
    {
        return x.Equals(y);
    }

    private bool CompareKeyEquality(T x, T y)
    {
        object xKey = Data.GetObjectKey(x);
        object yKey = Data.GetObjectKey(y);
        return xKey.Equals(yKey);
    }

}
0 голосов
/ 29 сентября 2008

Не существует встроенного способа сделать это, наиболее близким, что я могу вспомнить, является способ, которым DataTable обрабатывает новые и удаленные элементы.

То, что @ Джеймс Керран предлагает, - это просто заменить объект originalList объектом newList. Он выведет старый список, но сохранит переменную (т. Е. Указатель все еще там).

В любом случае, вам следует подумать, если оптимизация - это хорошо потраченное время. Если большая часть времени выполнения тратится на копирование значений из одного списка в другой, это может стоить того. Если это не так, а скорее какая-то преждевременная оптимизация, которую вы делаете, вы должны игнорировать это.

Потратьте время на полировку графического интерфейса пользователя или профилирования приложения, прежде чем начинать оптимизацию, - это мои $ .02.

0 голосов
/ 29 сентября 2008
List<int> firstList = new List<int>() {1, 2, 3, 4, 5};
List<int> secondList = new List<int>() {1, 3, 5, 7, 9};

List<int> newList = new List<int>();

foreach (int i in firstList)
{
  newList.Add(i);
}

foreach (int i in secondList)
{
  if (!newList.Contains(i))
  {
    newList.Add(i);
  }
}

Не очень чисто - но работает.

0 голосов
/ 29 сентября 2008

Первоначально я думал, что вы можете вызвать originalList.AddRange (newList), а затем удалить дубликаты, но я не уверен, что это будет более эффективно, чем очистка списка и его повторное заполнение.

...