Вопрос по c# – Определение дубликатов в таблице данных

1

У меня есть таблица данных, которую я загрузил из файла CSV. Мне нужно определить, какие строки являются дубликатами на основе двух столбцов product_id а такжеowner_org_id) в данных. После того как я определил это, я могу использовать эту информацию для построения своего результата, который представляет собой таблицу данных, содержащую только строки, которые не являются уникальными, и таблицу данных, содержащую только строки, которые являются уникальными.

Я рассмотрел другие примеры, приведенные здесь, и код, который я создал до сих пор, компилируется и выполняется, но кажется, что каждая строка данных уникальна. На самом деле в тестовых данных 13 строк, и только 6 уникальны. Ясно, что я делаю что-то не так.

РЕДАКТИРОВАТ: Я подумал, что строки с дубликатами должныВС удаляются, а не только дубликаты этой строки. например, если есть 4 дубликата, все 4 должны быть удалены, а не 3, оставляя одну уникальную строку из 4.

EDIT2: В качестве альтернативы, если я могу выбрать все дублирующиеся строки (вместо того, чтобы пытаться выбрать уникальные строки), это нормально для меня. В любом случае, я могу получить конечный результат.

Код в методе обработки:

MyRowComparer myrc = new MyRowComparer();
var uniquerows = dtCSV.AsEnumerable().Distinct(myrc);

наряду со следующим:

public class MyRowComparer : IEqualityComparer<DataRow>
{
    public bool Equals(DataRow x, DataRow y)
    {
        //return ((string.Compare(x.Field<string>("PRODUCT_ID"),   y.Field<string>("PRODUCT_ID"),   true)) ==
        //        (string.Compare(x.Field<string>("OWNER_ORG_ID"), y.Field<string>("OWNER_ORG_ID"), true)));
        return
            x.ItemArray.Except(new object[] { x[x.Table.Columns["PRODUCT_ID"].ColumnName] }) ==
            y.ItemArray.Except(new object[] { y[y.Table.Columns["PRODUCT_ID"].ColumnName] }) &&
            x.ItemArray.Except(new object[] { x[x.Table.Columns["OWNER_ORG_ID"].ColumnName] }) ==
            y.ItemArray.Except(new object[] { y[y.Table.Columns["OWNER_ORG_ID"].ColumnName] });
    }

    public int GetHashCode(DataRow obj)
    {
        int y = int.Parse(obj.Field<string>("PRODUCT_ID"));
        int z = int.Parse(obj.Field<string>("OWNER_ORG_ID"));
        int c = y ^ z;
        return c;
    }
}
Я не понимаю, почему ты используешьExcept - почему вы просто не сравниваете значения для двух важных столбцов? Такжеx.Table.Columns["PRODUCT_ID"].ColumnName должен быть идентичен «PRODUCT_ID» по определению, поэтому вы можете пропустить поиск по столбцу. PinnyM

Ваш Ответ

2   ответа
3

Вы можете использовать LINQ-To-DataSet иEnumerable.Except/Intersect:

var tbl1ID = tbl1.AsEnumerable()
        .Select(r => new
        {
            product_id = r.Field<String>("product_id"),
            owner_org_id = r.Field<String>("owner_org_id"),
        });
var tbl2ID = tbl2.AsEnumerable()
        .Select(r => new
        {
            product_id = r.Field<String>("product_id"),
            owner_org_id = r.Field<String>("owner_org_id"),
        });


var unique = tbl1ID.Except(tbl2ID);
var both = tbl1ID.Intersect(tbl2ID);

var tblUnique = (from uniqueRow in unique
                join row in tbl1.AsEnumerable()
                on uniqueRow equals new
                {
                    product_id = row.Field<String>("product_id"),
                    owner_org_id = row.Field<String>("owner_org_id")
                }
                select row).CopyToDataTable();
var tblBoth = (from bothRow in both
              join row in tbl1.AsEnumerable()
              on bothRow equals new
              {
                  product_id = row.Field<String>("product_id"),
                  owner_org_id = row.Field<String>("owner_org_id")
              }
              select row).CopyToDataTable();

Редактироват: Очевидно, я немного неправильно понял ваше требование. Таким образом, у вас есть только одинDataTable и хочу получить все уникальные и все повторяющиеся строки, это еще проще. Вы можете использоватьEnumerable.GroupBy с анонимным типом, содержащим оба поля:

var groups = tbl1.AsEnumerable()
    .GroupBy(r => new
    {
        product_id = r.Field<String>("product_id"),
        owner_org_id = r.Field<String>("owner_org_id")
    });
var tblUniques = groups
    .Where(grp => grp.Count() == 1)
    .Select(grp => grp.Single())
    .CopyToDataTable();
var tblDuplicates = groups
    .Where(grp => grp.Count() > 1)
    .SelectMany(grp => grp)
    .CopyToDataTable();
В начале у меня есть только один источник данных, в котором есть все. Этот пример предполагает, что у меня уже есть все, что разбито на два набора данных, или я что-то упустил? user1366062
@ user1366062: Тогда я неправильно понял ваше требование. Отредактировал мой ответ. Rango
Второе редактирование отлично работает! Благодарность user1366062
Идеально, это как раз то, что я искал. Благодарность mack
1

которые вам не интересны Except исключает) в.

Вместо этого будьте максимально понятны (тип данных) и сохраняйте их простыми:

public bool Equals(DataRow x, DataRow y)
{   
    // Usually you are dealing with INT keys
    return (x["PRODUCT_ID"] as int?) == (y["PRODUCT_ID"] as int?)
      && (x["OWNER_ORG_ID"] as int?) == (y["OWNER_ORG_ID"] as int?);

    // If you really are dealing with strings, this is the equivalent:
    // return (x["PRODUCT_ID"] as string) == (y["PRODUCT_ID"] as string)
    //  && (x["OWNER_ORG_ID"] as string) == (y["OWNER_ORG_ID"] as string)
}  

Проверитьnull если это возможно. Возможно, вы хотите исключить строки, которые равны, потому что их идентификаторы равны нулю.

Соблюдайтеint?. Это не опечатка. Вопросительный знак необходим, если вы имеете дело со значениями базы данных из столбцов, которые могут бытьNULL. Причина в том, чтоNULL значения будут представлены типомDBNull в C #. Используяas оператор просто дает вамnull в этом случае (вместоInvalidCastException. Если вы уверены, вы имеете дело сINT NOT NULL, заклинание с(int).

То же самое относится и к строкам.(string) утверждает, что вы ожидаете ненулевые значения БД.

EDIT1:

Неправильный тип. ItemArray не является хеш-таблицей. Используйте строку напрямую.

EDIT2:

Добавленоstring пример, какой-то комментарий

Для более простого пути, проверьте Как выбрать отдельные строки в таблице данных и сохранить в массиве

EDIT3:

Некоторое объяснение касательно слепков.

Другая ссылка, которую я предложил, делает то же самое, что и ваш код. Я забыл ваше первоначальное намерение ;-) Я только что увидел ваш код и ответил на самую очевидную ошибку, которую я видел - извините

Вот как бы я решил проблему

using System.Linq;
using System.Data.Linq;

var q = dtCSV
    .AsEnumerable()
    .GroupBy(r => new { ProductId = (int)r["PRODUCT_ID"], OwnerOrgId = (int)r["OWNER_ORG_ID"] })
    .Where(g => g.Count() > 1).SelectMany(g => g);

var duplicateRows = q.ToList();

Я не знаю, правильно ли это на 100%, у меня нет IDE под рукой. И вам нужно будет настроить приведение к соответствующему типу. Смотрите мое дополнение выше.

Похоже, что удаляются все дубликаты, кроме одного на группу дубликатов. например, если у меня есть 5 строк с одинаковыми product_id и owner_org_id, я получу 1 строку с соответствующими product_id и owner_org_id. Я также хотел бы удалить этот последний ряд. user1366062
Кроме того, я не мог использовать «as int», хотя они int, поскольку я получаю сообщение об ошибке в имени столбца, которое говорит, что «оператор as должен использоваться со ссылочным типом или обнуляемым типом». Я проверил вашу ссылку для более простого способа, который, казалось, частично работал, однако у меня были проблемы с тем, чтобы он возвращал всю строку, а не только два столбца, о которых идет речь. Мне нужна вся строка, но только для сравнения двух столбцов для дубликатов. Я мог бы сделать это неправильно, но пример казался довольно ясным. user1366062
@ user1366062: Я ответил на оба ваших комментария в своем ответе. В середине есть часть о приведениях и добавление в конц skarmats
Только что увидел изменения в другом ответе. Возьмите его код для более полного решения. Он берет полученные строки и возвращает их обратно вDataTable что может быть тем, что ты хочешь. skarmats

Похожие вопросы