Сравните два списка <int>

Я пишу небольшую программу для сравнения двух списков. Если значения одинаковы, я добавляю их в список dups, если они разные, я добавляю их в разные. Я заметил, что некоторые из моих значений добавлены, а некоторые нет, и после некоторой отладки я не уверен, в чем проблема. Может кто-то пролить немного света? Спасибо.

        List<int> groupA = new List<int>();
        List<int> groupB = new List<int>();

        List<int> dups = new List<int>();
        List<int> distinct = new List<int>();

        groupA.Add(2);
        groupA.Add(24);
        groupA.Add(5);
        groupA.Add(72);
        groupA.Add(276);
        groupA.Add(42);
        groupA.Add(92);
        groupA.Add(95);
        groupA.Add(266);
        groupA.Add(42);
        groupA.Add(92);


        groupB.Add(5);
        groupB.Add(42);
        groupB.Add(95);

        groupA.Sort();
        groupB.Sort();

        for (int a = 0; a < groupA.Count; a++)
        {
            for (int b = 0; b < groupB.Count; b++)
            {
                groupA[a].CompareTo(groupB[b]);


                if (groupA[a] == groupB[b])
                {
                    dups.Add(groupA[a]);
                    groupA.Remove(groupA[a]);
                    groupB.Remove(groupB[b]);
                }

            }
            distinct.Add(groupA[a]);
        }

Ответ 1

Я бы использовал методы Intersect и Except:

dups = groupA.Intersect(groupB).ToList();
distinct = groupA.Except(groupB).ToList();

Ответ 2

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

Например, следующий код неверен

List<int> nums = new List<int>{2, 4, 6, 7, 8, 10, 11};

for (int i = 0; i < nums.Count; i++)
{
  if (nums[i] % 2 == 0)
    nums.Remove(nums[i]);
}

Если вернет список {4, 7, 10, 11} вместо {7, 11}.

Он не удалит значение 4, потому что, когда я удаляю значение 2, (для i=0) список nums идет от

//index 0  1  2  3  4   5   6 
nums = {2, 4, 6, 7, 8, 10, 11}

to

//index 0  1  2  3  4   5
nums = {4, 6, 7, 8, 10, 11}

Цикл заканчивается, я увеличивается на 1, а следующий элемент ссылается на nums[1], который не является 4, как можно было бы интуитивно ожидать, но 6. Таким образом, значение 4 пропускается, а проверка не выполняется.

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

List<int> nums = new List<int>{2, 4, 6, 7, 8, 10, 11};

int i = 0;
while (i < nums.Count)
{
  if (nums[i] % 2 == 0)
  {
    nums.Remove(nums[i])
  }      
  else
  {
    i++; //only increment if you are not removing an item
         //otherwise re-run the loop for the same value of i
  }  
}

вы могли бы даже разветкить for, например

for (int i = 0; i < nums.Count; i++)
{
  if (nums[i] % 2 == 0)
  {
    nums.Remove(nums[i]);
    i--; //decrement the counter, so that it will stay in place
         //when it is incremented at the end of the loop
  }
}

В качестве альтернативы вы можете использовать linq, например:

distinct.AddRange(groupA);
distinct.AddRange(groupB);
distinct = distinct.Distinct().ToList();

и

dups.AddRange(groupA);
dups.AddRange(groupB);

dups = dups.GroupBy(i => i)
           .Where(g => g.Count() > 1)
           .Select(g => g.Key)
           .ToList();

Обратите внимание, что код LINQ не будет изменять существующие списки groupA и groupB. Если вы просто хотите отличить их, вы можете просто сделать

groupA = groupA.Distinct().ToList();
groupB = groupB.Distinct().ToList();

Ответ 3

Вы можете легко сделать это с помощью Linq:

    List<int> dups = groupA.Intersect(groupB).ToList();
    List<int> distinct = groupA.Except(groupB).ToList();

(предполагая, что я правильно понял, что вы пытались сделать)