Удаление дубликатов в одном наборе данных относительно другого в C #

Я новичок в C #. Пытаюсь удалить дубликаты в CollectionIn1, но это не работает. В CollectionIn не удаляются дубликаты.

Чтобы уточнить, у collectionIn есть [A, B, C, D], а у collectionIn2 есть [A, B, C].

Итак, я хочу удалить значения (A, B, C) в collectionIn

for (int i = 0; i < CollectionIn.Rows.Count; i++) {
    string value1 = CollectionIn.Rows[i].ItemArray[0].ToString().ToLower(); 

    for (int i2 = 0; i2 < CollectionIn2.Rows.Count; i2++) {
        string value2 = CollectionIn2.Rows[i2].ItemArray[0].ToString().ToLower(); 

        if (value1 == value2) {
            //Remove value1 when value1 == value2
            CollectionIn.Rows[i].Delete(); <--- Trying to delete when there is duplicate in both collections

            CollectionIn.AcceptChanges();
        }
    }
    //CollectionOut.Rows.Add(value1);
}

Я внес некоторые изменения по этой ссылке http://www.rpaforum.net/threads/how-to-compare-two-excel-sheet-using-c-code-in-blueprism.897/


person JAYY    schedule 25.06.2018    source источник


Ответы (4)


Сравнение двух наборов может иметь сложность O (n2). Это плохо. Вы можете улучшить это, если у вас есть начальный поиск хэша.

var Set1 = new Dictionary<string, int>();

//Prehash all values in the set that won't be deleted from 
for (int i = 0; i < CollectionIn.Rows.Count; i++)
{
    string value1 = CollectionIn.Rows[i].ItemArray[0].ToString().ToLower();
    Set1.Add(value1, i);
}

//Loop over the other set
for (int i2 = 0; i2 < CollectionIn2.Rows.Count; i2++)
{
    string value2 = CollectionIn2.Rows[i2].ItemArray[0].ToString().ToLower();

    int foundIndex;
    if (Set1.TryGetValue(value2, out foundIndex) == false)
        continue;

    //Remove value1 when value1 == value2
    CollectionIn.Rows[foundIndex].Delete();
}
CollectionIn.AcceptChanges(); //It's probably best to save changes last as a single call

Я хэшировал CollectionIn, а затем повторил CollectionIn2. Это означает, что мне нужен словарь, поэтому у меня будет индекс CollectionIn для удаления. Если бы это было отменено, и CollectionIn2 был хеширован, это должен был бы быть только хэш-набор, и было бы лучше, потому что он мог бы обрабатывать внутренние дубликаты в наборе CollectionIn, поэтому:

var Set2 = new HashSet<string>();

//Prehash all values in one set (ideally the larger set)
for (int i2 = 0; i2 < CollectionIn2.Rows.Count; i2++)
{
    string value2 = CollectionIn2.Rows[i2].ItemArray[0].ToString().ToLower();

    if (Set2.Contains(value2))
        continue; //Duplicate value
    else
        Set2.Add(value2);
}

//Loop over the other set
for (int i1 = 0; i1 < CollectionIn.Rows.Count; i1++)
{
    string value1 = CollectionIn.Rows[i1].ItemArray[0].ToString().ToLower();

    if (Set2.Contains(value1) == false)
        continue;

    //Remove value1 when value1 == value2
    CollectionIn.Rows[i1].Delete();
}

CollectionIn.AcceptChanges(); //It's probably best to save changes last as a single call

Этот шаблон применим ко многим типам наборов данных (включая список, массив и т. Д.). Конечно, лучше, если вы можете написать SQL для удаленных наборов данных в той же базе данных.

Если вам нравятся лямбда-функции, они должны выглядеть примерно так:

var alreadyInSet2 = new HashSet<string>(CollectionIn2.Rows.Cast<DataRow>()
                    .Select(x => x[0].ToString().ToLower()));

CollectionIn.Rows.Cast<DataRow>()
                    .Where(y => alreadyInSet2.Contains(y[0].ToString().ToLower()) == false)
                    .ToList() //I think you technically need this before calling ForEach
                    .ForEach(y => y.Delete());

CollectionIn.AcceptChanges();                   

См. Также: С двумя очень большими списками / коллекциями - как эффективно обнаруживать и / или удалять дубликаты - где больше времени / работы можно потратить на более широкий набор ответов и повышение производительности.

person Todd    schedule 25.06.2018
comment
Можете ли вы объяснить, почему AcceptChanges вызывается при каждом удалении вместо того, чтобы поднимать его за пределы цикла? Также я ожидал, что это сделает индексы недействительными, поскольку коллекция Rows изменилась. - person Zer0; 25.06.2018
comment
@ Zer0 Потому что так был код OP, но эта деталь не является частью вопроса. Однако я только что обновил свой ответ обратной стратегией, которая, вероятно, лучше, и я заметил этот повторный вызов, как вы указали. - person Todd; 25.06.2018
comment
Я спросил, потому что думаю, что это вводит ошибку, которая сводится к изменению коллекции при повторении ее. Не просто производительность. - person Zer0; 25.06.2018
comment
Ура, без проблем. - person Todd; 25.06.2018
comment
+1 Хотя это более подробное решение, это определенно более быстрое решение, чем мое собственное, которое масштабируется тем лучше, чем больше количество строк. Отмечая это ради ОП. Простота, вероятно, лучше, если таблицы маленькие, а влияние на производительность незначительно, но это намного лучше, если они большие. - person Zer0; 25.06.2018
comment
Привет, это вызывает ошибку компилятора. Новое выражение требует (), [] или {] после типа. Я погуглил и понял, что, возможно, эта строка должна быть var Set2 = new HashSet ‹string› (); ? - person JAYY; 25.06.2018
comment
@JAYY Извините, я сделал это с помощью блокнота ++ - я не тестировал с компилятором. Исправлено построение объекта Dictionary и Hashset. - person Todd; 25.06.2018
comment
@JAYY Я бы посоветовал вам протестировать только мой последний фрагмент кода. - person Todd; 25.06.2018
comment
Понятно. Но я предпочитаю средний, потому что его легче понять кому-то вроде меня, у которого есть только некоторый фон java :) - person JAYY; 25.06.2018
comment
@JAYY Я обновил все 3 фрагмента. Теперь все они должны быть компилируемыми. - person Todd; 25.06.2018
comment
Большое спасибо! Но результат все равно неверный. Это та же проблема. Он ничего не удаляет из collection1. - person JAYY; 25.06.2018
comment
Это ошибка алгоритма или функция Delete не работает так, как вы ожидаете? Поставьте точку останова на вызов функции Delete и посмотрите, вызывается ли она когда-нибудь. - person Todd; 25.06.2018
comment
Я думаю, что алгоритм, вероятно, правильный, но функция удаления у меня не работает. Я использую синюю призму, поэтому думаю, что проблема в этом. - person JAYY; 25.06.2018
comment
@JAYY Я обнаружил логическую ошибку во втором фрагменте. Строка Contains теперь включает == false. - person Todd; 25.06.2018
comment
Позвольте нам продолжить это обсуждение в чате. - person Todd; 25.06.2018

Вы можете удалить дубликаты с помощью Distinct оператор.

Чтобы удалить дубликаты из чего-то вроде IList ‹>, вы можете сделать:

yourList.RemoveAll( yourList.Except( yourList.Distinct() ) );
person Fandango68    schedule 25.06.2018
comment
Привет, так что это CollectionIn.RemoveAll (CollectionIn2.Except (CollectionIn.Distinct ())); ? Чтобы уточнить, у collectionIn есть [A, B, C, D], а у collectionIn2 есть [A, B, C]. Так это поможет удалить значения (A, B, C) в collectionIn? - person JAYY; 25.06.2018
comment
Это неправильный ответ. Это сравнивает только один набор. ОП имеет в виду сравнение двух наборов. - person Todd; 25.06.2018
comment
@Todd, без кормления ложкой OP, очевидно, должен объединить два набора в один, чтобы вышеперечисленное работало. Это дать ему ключ к разгадке. - person Fandango68; 25.06.2018
comment
@ Fandango68 Возможно, вы правы, но, учитывая то, что написано в сообщении OP и как эта статья будет найдена и повторно использована в будущем, я не думаю, что подобного рода намеки приветствуются в сообществе stackoverflow. Просто так оно и есть. - person Todd; 25.06.2018
comment
@Todd хочет указать на конкретное руководство, которое подкрепляет это? Я понимаю SO - это как обучать людей, так и помогать им с техническими проблемами, а не кормить с ложечки. Прочтите meta.stackexchange.com/questions / 183829 / и ответ Барта ... Вы можете немного перефразировать это. Ложное вскармливание не имеет положительного тона, и это, конечно, не то, что я бы назвал лучшим в этом сообществе. Для меня слово «кормление с ложечки» указывает на лень со стороны ОП, что мы, безусловно, не одобряем. - person Fandango68; 25.06.2018
comment
@ Fandango68 Я использовал термин сообщество, а не правила. Лично я использую SO для кормления ложкой. Я все время копирую в своей работе. Я также отвечаю на свои вопросы для будущего использования. Я почти уверен, что в этой ситуации ваш ответ не соответствует спецификации OP - это довольно четкий IMO. - person Todd; 25.06.2018
comment
@ Fandango68 Взгляните на этот вопрос [stackoverflow.com/questions/51016873/, и посмотрите, что я имею в виду сообществом. Такие люди, как вы, искренне пытаются помочь, но их отвергают. - person Todd; 25.06.2018

foreach(var row in CollectionIn.Rows.Cast<DataRow>()
    .Where(x => CollectionIn2.Rows.Cast<DataRow>()
    .Any(y => y[0].ToString().ToLower() == x[0].ToString().ToLower())))
{
    row.Delete();
}
CollectionIn.AcceptChanges();

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

Также в вашем коде есть ошибка из-за изменения коллекций во время итерации по ним.

person Zer0    schedule 25.06.2018
comment
Я думаю, что вас не хватает (в строке 3 - person JAYY; 25.06.2018
comment
@JAYY Исправлено - person Zer0; 25.06.2018
comment
Да, наверное, красиво и просто, если сравнивать всего несколько наборов объектов. - person Todd; 25.06.2018
comment
@Todd Ага. Именно поэтому я отметил влияние на производительность. - person Zer0; 25.06.2018

Он работает, и его легко понять.

List<string> List1 = new List<string> { "A", "B", "C", "D" };
List<string> List2 = new List<string> { "A", "B", "C" };
List<string> ListTemp = new List<string>();

foreach (string str1 in List1)
{
     foreach (string str2 in List2)
     {
          if (str1 == str2)
          {
               ListTemp.Add(str1);
          }
     }
 }            

foreach (string temp in ListTemp)
{
     List1.Remove(temp);
}
person William Lee    schedule 25.06.2018