ما هو أقل قدر من التعليمات البرمجية اللازمة لتحديث قائمة واحدة بقائمة أخرى؟

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

سؤال

لنفترض أن لدي قائمة واحدة:

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

كيف يمكنني تحديث القائمة الأصلية بحيث:

  1. إذا ظهر int في قائمة جديدة، فاحتفظ به
  2. إذا لم يظهر int في القائمة الجديدة، فقم بإزالته
  3. أضف أي ints من newList إلى originalList غير الموجودة بالفعل

وبالتالي - جعل محتويات القائمة الأصلية:

{ 1, 5, 7, 11 }

سبب سؤالي هو أن لدي شيئًا به مجموعة من الأطفال.عندما يقوم المستخدم بتحديث هذه المجموعة، بدلاً من مجرد حذف جميع العناصر الفرعية، ثم إدراج تحديداتهم، أعتقد أنه سيكون أكثر كفاءة إذا تصرفت فقط مع العناصر الفرعية التي تمت إضافتها أو إزالتها، بدلاً من تمزيق المجموعة بأكملها، وإدراج المجموعة بأكملها. قائمة جديدة للأطفال كما لو كانوا جميعًا جددًا.

تحرير - آسف - لقد كتبت عنوانًا فظيعًا ...كان يجب أن أكتب "أقل قدر من التعليمات البرمجية" بدلاً من "فعال".أعتقد أن هذا أدى إلى التخلص من الكثير من الإجابات التي حصلت عليها.كلهم رائعون...شكرًا لك!

هل كانت مفيدة؟

المحلول

آسف، كتبت ردي الأول قبل أن أرى فقرتك الأخيرة.

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

نصائح أخرى

originalList = newList;

أو إذا كنت تفضل أن تكون قوائم متميزة:

originalList = new List<int>(newList);

ولكن في كلتا الحالتين تفعل ما تريد.وفقًا لقواعدك، بعد التحديث، ستكون القائمة الأصلية مطابقة للقائمة الجديدة.

تحديث:أشكركم جميعًا على دعم هذه الإجابة، ولكن بعد قراءة أقرب للسؤال، أعتقد أن إجابتي الأخرى (أدناه) هي الإجابة الصحيحة.

إذا كنت تستخدم بعض طرق امتداد 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()));

إذا لم تكن قلقًا بشأن الترتيب النهائي، فمن المرجح أن يكون Hashtable/HashSet هو الأسرع.

حل لينك:

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

كانت فكرتي الأولية هي أنه يمكنك الاتصال بـ originalList.AddRange(newList) ثم إزالة التكرارات - لكنني لست متأكدًا مما إذا كان ذلك سيكون أكثر كفاءة من مسح القائمة وإعادة نشرها.

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

ليست نظيفة للغاية - ولكنها تعمل.

لا توجد طريقة مدمجة للقيام بذلك، وأقرب ما يمكنني التفكير فيه هو الطريقة التي يتعامل بها DataTable مع العناصر الجديدة والمحذوفة.

ماذا @ جيمس كوران ما يقترحه هو مجرد استبدال كائن القائمة الأصلية بكائن القائمة الجديدة.سيتم تفريغ القائمة القديمة، مع الاحتفاظ بالمتغير (أي.المؤشر لا يزال هناك).

وبغض النظر عن ذلك، يجب أن تفكر فيما إذا كان تحسين هذا الوقت قد تم إنفاقه بشكل جيد.إذا تم قضاء معظم وقت التشغيل في نسخ القيم من قائمة إلى أخرى، فقد يكون الأمر يستحق ذلك.إذا لم يكن الأمر كذلك، بل بعض التحسينات المبكرة التي تقوم بها، فيجب عليك تجاهلها.

قضاء بعض الوقت في صقل واجهة المستخدم الرسومية أو ملف تعريف التطبيق قبل البدء في التحسين هو 0.02 دولار.

هذه مشكلة شائعة يواجهها المطورون عند كتابة واجهات المستخدم للحفاظ على علاقات قاعدة بيانات متعددة الأطراف.لا أعرف مدى فعالية هذا، لكنني كتبت فئة مساعدة للتعامل مع هذا السيناريو:

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

}

إذا كنت تستخدم .Net 3.5

var List3 = List1.Intersect(List2);

إنشاء قائمة جديدة تحتوي على تقاطع القائمتين، وهو ما أعتقد أنك تستهدفه هنا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top