Quelle est la quantité minimale de code nécessaire pour mettre à jour une liste avec une autre liste?

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

Question

Supposons que je n'ai qu'une liste:

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

Et une autre liste ...

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

Comment puis-je mettre à jour originalList pour que:

  1. Si l'entier apparaît dans newList, conservez
  2. Si l'entier n'apparaît pas dans newList, supprimez
  3. Ajoutez toutes les entrées de newList dans originalList qui n'y sont pas déjà

Ainsi, création du contenu de la liste originale:

{ 1, 5, 7, 11 }

Je pose la question parce que j’ai un objet avec une collection d’enfants. Lorsque l'utilisateur met à jour cette collection, au lieu de simplement supprimer tous les enfants, puis d'insérer leurs sélections, je pense qu'il serait plus efficace d'agir uniquement sur les enfants ajoutés ou supprimés, plutôt que de détruire l'ensemble de la collection et d'insérer le fichier. newList enfants comme s'ils étaient tous nouveaux.

EDIT - Désolé - J'ai écrit un titre horrible ... J'aurais dû écrire "moins de code" au lieu de "efficace". Je pense que cela a jeté beaucoup de réponses que j'ai obtenues. Ils sont tous géniaux ... merci!

Était-ce utile?

La solution

Désolé, j'ai écrit ma première réponse avant d'avoir vu votre dernier paragraphe.

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

Autres conseils

originalList = newList;

Ou si vous préférez qu'il s'agisse de listes distinctes:

originalList = new List<int>(newList);

Mais, de toute façon, fait ce que vous voulez. Selon vos règles, après la mise à jour, originalList sera identique à newList.

MISE À JOUR: je vous remercie tous pour le soutien apporté à cette réponse, mais après une lecture plus attentive de la question, je pense que mon autre réponse (ci-dessous) est la bonne.

Si vous utilisez certaines méthodes d'extension LINQ, vous pouvez le faire en deux lignes:

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

Cela suppose (comme le font les solutions des autres personnes) que vous avez substitué Equals dans votre objet d'origine. Mais si vous ne pouvez pas remplacer Equals pour une raison quelconque, vous pouvez créer un IEqualityOperator comme ceci:

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

Les lignes ci-dessus deviennent alors:

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

Et si vous passez de toute façon dans IEqualityOperator, vous pouvez raccourcir la deuxième ligne:

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

Si vous ne vous inquiétez pas de la commande éventuelle, un Hashtable / HashSet sera probablement le plus rapide.

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

Je pensais initialement que vous pouviez appeler originalList.AddRange (newList), puis supprimer les doublons, mais je ne suis pas sûr que ce serait plus efficace que d'effacer la liste et de la repeupler.

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

Pas très propre - mais ça marche.

Il n’existe pas de méthode intégrée pour ce faire. Le plus proche que je puisse imaginer est la manière dont DataTable gère les éléments nouveaux et supprimés.

Qu'est-ce que @James Curran suggère de simplement remplacer l’objet originalList par l’objet newList. Il videra l'ancienne liste, mais conservera la variable (le pointeur est toujours présent).

Quoi qu’il en soit, vous devez vous demander si l’optimisation de ce temps est un bon investissement. Si la majorité du temps d’exécution est consacré à la copie des valeurs d’une liste à l’autre, cela peut valoir la peine. Si ce n’est pas le cas, mais plutôt une optimisation prématurée que vous faites, vous devriez l’ignorer.

Passez mon temps à peaufiner l'interface graphique ou à profiler l'application avant de commencer à optimiser

Il s'agit d'un problème courant rencontré par les développeurs lors de la rédaction d'interfaces utilisateur afin de maintenir des relations de base de données multiples. Je ne sais pas à quel point c'est efficace, mais j'ai écrit une classe d'assistance pour gérer ce scénario:

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

}

si vous utilisez .Net 3.5

var List3 = List1.Intersect(List2);

Crée une nouvelle liste contenant l'intersection des deux listes. C'est ce que je pense que vous visez ici.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top