LINQ-запрос для обнаружения дублирующих свойств в списке объектов - PullRequest
4 голосов
/ 16 декабря 2009

У меня есть список объектов. Эти объекты состоят из пользовательского класса, который в основном содержит два строковых поля String1 и String2.

Что мне нужно знать, так это то, дублируется ли какая-либо из этих строк в этом списке. Поэтому я хочу знать, если objectA.String1 == objectB.String1, или ObjectA.String2 == ObjectB.String2, или ObjectA.String1 == ObjectB.String ", или ObjectA.String2 == ObjectB.String1.

Кроме того, я хочу пометить каждый объект, который содержит повторяющуюся строку, как дублирующую строку (с bool HasDuplicate на объекте).

Так что, когда обнаружение дублирования будет запущено, я хочу просто выполнить поиск по списку следующим образом:

foreach (var item in duplicationList)
    if (item.HasDuplicate)
        Console.WriteLine("Duplicate detected!");

Казалось, это хорошая проблема, которую нужно решить с помощью LINQ, но я не могу до конца понять хороший запрос. Так что я решил это, используя «старый добрый» foreach, но я все еще заинтересован в версии LINQ.

Ответы [ 5 ]

12 голосов
/ 16 декабря 2009

Вот полный пример кода, который должен работать для вашего случая.

class A
{
    public string Foo   { get; set; }
    public string Bar   { get; set; }
    public bool HasDupe { get; set; }
}

var list = new List<A> 
          { 
              new A{ Foo="abc", Bar="xyz"}, 
              new A{ Foo="def", Bar="ghi"}, 
              new A{ Foo="123", Bar="abc"}  
          };

var dupes = list.Where(a => list
          .Except(new List<A>{a})
          .Any(x => x.Foo == a.Foo || x.Bar == a.Bar || x.Foo == a.Bar || x.Bar == a.Foo))
          .ToList();

dupes.ForEach(a => a.HasDupe = true);
4 голосов
/ 17 декабря 2009

Это должно работать:

public class Foo
{
    public string Bar;
    public string Baz;
    public bool HasDuplicates;
}

public static void SetHasDuplicate(IEnumerable<Foo> foos)
{
    var dupes = foos
        .SelectMany(f => new[] { new { Foo = f, Str = f.Bar }, new { Foo = f, Str = f.Baz } })
        .Distinct() // Eliminates double entries where Foo.Bar == Foo.Baz
        .GroupBy(x => x.Str)
        .Where(g => g.Count() > 1)
        .SelectMany(g => g.Select(x => x.Foo))
        .Distinct()
        .ToList();

    dupes.ForEach(d => d.HasDuplicates = true);    
}

То, что вы в основном делаете, это

  1. SelectMany: создать список всех строк с сопровождающими их Foo
  2. Отличительный: Удалить двойные записи для одного и того же экземпляра Foo (Foo.Bar == Foo.Baz)
  3. GroupBy: группировка по строке
  4. Где: отфильтровать группы, содержащие более одного элемента. Они содержат дубликаты.
  5. SelectMany: вернуть foos из групп.
  6. Отличительный: Удалить двойные вхождения foo из списка.
  7. ForEach: установить свойство HasDuplicates.

Некоторые преимущества этого решения над решением Уинстона Смита:

  1. Легче расширить на большее количество строковых свойств. Предположим, что было 5 свойств. В его решении вам придется написать 125 сравнений, чтобы проверить наличие дубликатов (в предложении Any). В этом решении это просто вопрос добавления свойства в первый вызов selectmany.
  2. Производительность должна быть намного лучше для больших списков. Решение Уинстона перебирает список для каждого элемента в списке, в то время как это решение перебирает его только один раз. (Решение Уинстона - O (n²), а это O (n)).
0 голосов
/ 07 апреля 2016

Наконечник шляпы до https://stackoverflow.com/a/807816/492

var duplicates = duplicationList
                .GroupBy(l => l)
                .Where(g => g.Count() > 1)
                .Select(g => {foreach (var x in g)
                                 {x.HasDuplicate = true;}
                             return g;
                });

duplicates - это одноразовое решение, но оно поможет вам в меньших количествах.

0 голосов
/ 16 декабря 2009

Во-первых, если ваш объект еще не имеет свойства HasDuplicate, объявите метод расширения, который реализует HasDuplicateProperties:

public static bool HasDuplicateProperties<T>(this T instance)
    where T : SomeClass 
    // where is optional, but might be useful when you want to enforce
    // a base class/interface
{
    // use reflection or something else to determine wether this instance
    // has duplicate properties
    return false;
}

Вы можете использовать этот метод расширения в запросах:

var itemsWithDuplicates = from item in duplicationList
                          where item.HasDuplicateProperties()
                          select item;

То же самое работает с обычным свойством:

var itemsWithDuplicates = from item in duplicationList
                          where item.HasDuplicate
                          select item;

или

var itemsWithDuplicates = duplicationList.Where(x => x.HasDuplicateProperties());
0 голосов
/ 16 декабря 2009
var dups = duplicationList.GroupBy(x => x).Where(y => y.Count() > 1).Select(y => y.Key);

foreach (var d in dups)
    Console.WriteLine(d);
...