Qual é a menor quantidade de código necessário para atualizar um lista com outra lista?

StackOverflow https://stackoverflow.com/questions/148662

Pergunta

Suponha que eu tenho uma lista:

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

E outra lista ...

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

Como posso atualizar originalList de modo que:

  1. Se o int aparece na newList, mantenha
  2. Se o int não aparecer na newList, remove
  3. Adicione quaisquer ints de newList em originalList que não estão lá já

Assim - tornando o conteúdo de originalList:

{ 1, 5, 7, 11 }

A razão que eu estou pedindo é porque eu tenho um objeto com uma coleção de crianças. Quando o usuário atualiza essa coleção, em vez de apenas apagar todas as crianças, em seguida, inserir suas seleções, eu acho que seria mais eficiente se eu apenas agiu sobre as crianças que foram adicionados ou removidos, ao invés de derrubar toda a coleção, e inserindo o crianças newlist como se eles são todos novos.

EDIT - Desculpe - eu escrevi um título horrível ... Eu deveria ter escrito 'menos quantidade de código' em vez de 'eficiente'. Eu acho que jogou fora de um monte das respostas que eu comecei. Eles são todos grandes ... obrigado!

Foi útil?

Solução

Desculpe, escrevi minha primeira resposta antes que eu vi o seu último parágrafo.

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);
}

Outras dicas

originalList = newList;

Ou se você preferir eles serem listas distintas:

originalList = new List<int>(newList);

Mas, de qualquer forma faz o que quiser. Pelas suas regras, após a atualização, originalList será idêntico ao newList.

UPDATE:. Agradeço a todos pelo apoio desta resposta, mas depois de um mais perto de leitura da pergunta, eu acredito que a minha outra resposta (abaixo) é o correto

Se você usar alguns métodos de extensão LINQ, você pode fazê-lo em duas linhas:

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

Isso pressupõe (como fazem as soluções de outras pessoas) que você tem Equals substituído em seu objeto original. Mas se você não pode substituir Equals por algum motivo, você pode criar um IEqualityOperator assim:

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();
    }
}

Em seguida, as linhas acima tornam-se:

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

E se você está passando em um IEqualityOperator qualquer maneira, você pode fazer a segunda linha ainda mais curto:

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

Se você não está preocupado com a eventual ordenação, um Hashtable / HashSet provavelmente será o mais rápido.

solução 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);

Meu pensamento inicial era que você poderia chamar originalList.AddRange (newList) e remova as duplicatas -. Mas eu não tenho certeza se isso seria mais eficiente do que limpar a lista e repovoamento it

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);
  }
}

Não muito limpo -. Mas funciona

Não há construído em forma de fazer isso, o mais próximo que eu posso pensar é a forma como as alças DataTable novas e os itens excluídos.

O @ James Curran sugere é simplesmente substituir o objeto originalList com o objeto newList. Ele vai despejar o oldList, mas manter a variável (ou seja, o ponteiro ainda está lá).

Independentemente disso, você deve considerar se otimizar isso é tempo bem gasto. É a maior parte do tempo de execução passou valores de cópia de uma lista para a próxima, pode valer a pena. Se não for, mas sim alguns otimização prematura que você está fazendo, você deve ignorá-lo.

Gastar tempo polindo o GUI ou o perfil do aplicativo antes de começar a otimizar é my $ .02.

Este é um problema comum desenvolvedores encontrar ao escrever UIs para manter muitos-para-muitos relacionamentos de banco de dados. Eu não sei o quão eficiente este é, mas eu escrevi uma classe auxiliar para lidar com este cenário:

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);
    }

}

Se o seu usando Net 3.5

var List3 = List1.Intersect(List2);

Cria uma nova lista que contém a intersecção das duas listas, que é o que eu acredito que você está atirando para aqui.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top