LINQ VB如何在对象列表中检查重复项

3
我有一个对象列表,每个对象都有两个相关属性:“ID”和“Name”。我们称之为“lstOutcomes”列表。我需要检查列表中的重复项(即object1.ID = object2.ID等),如果至少有一个重复项,则设置标志(例如:valid=false),还要向用户发送一条消息,提到失败时的“Name”名称。
我确定我需要使用Group By算子来完成这个任务,但我不习惯在LINQ中这样做,而且现有的示例并没有帮助我。 这篇文章似乎接近我所需要的,但它是用C#写的。下面是一个初步的尝试...
Dim duplist = _
    (From o As objectType In lstOutcomes _
    Group o By o.ID Into g = Group _
    Let dups = g.Where(Function(h) g.Count > 1) _
    Order By dups Descending).ToArray

if duplist.count > 0 then
valid = false
end if

help?


请访问 http://blogs.msdn.com/b/ericwhite/archive/2008/08/19/find-duplicates-using-linq.aspx - undefined
@Magnus,你的文章是关于C#的...这正是我试图解决的问题的一半! - undefined
我的答案有效...如果没有重复项,valid保持为true - undefined
5个回答

2

我会用C#编写,但希望你能将其转换为VB。它不使用join方法,而是O(n log n)的时间复杂度,我假设你有一个 List<T>

lst.Sort();  //O(nlogn) part.

var duplicatedItems = lst.Skip(1).Where((x,index)=>x.ID == lst[index].ID);

Sort方法只有在类实现了IComparable接口时才能工作。 - undefined
此外,“duplicatedItems”列表中不会包含第一个重复的项目,只会包含具有相同ID的后续项目(尽管这可能是可以接受的)。 - undefined
@Meta-Knight,是的,但我会假设第一个数字是有效的,之后的任何相同数字都是无效的,并且问题只是要求检查重复项是否存在。如果可以使用索引的Any,我会使用它。 - undefined

2

虽然已经晚了,但这对他人可能有所帮助。

您可以使用一对非常简洁的一行代码来实现此目标:

Dim lstOutcomes As IList(Of T)

Dim FoundDuplicates As Boolean
FoundDuplicates = lstOutcomes.Any(Function(p) lstOutcomes.ToArray.Count(Function(q) p.ID = q.ID and p.Name=q.Name) > 1)

Dim ListOfDuplicates As IList(Of T)
ListOfDuplicates = lstOutcomes.Where(Function(p) lstOutcomes.ToArray.Count(Function(q) p.ID = q.ID And p.Name = q.Name) > 1)

然后,您可以清除重复项列表,使其仅包含一次重复项:
Dim CleanList as IList(of T)
For Each MyDuplicate As T in ListOfDuplicates
    If not CleanList.Any(function(p) p.ID = MyDuplicate.ID And p.Name = MyDuplicate.Name) then
        CleanList.Add(MyDuplicate)
    End If
Next

或者作为一行代码,虽然它看起来不太好理解

ListOfDuplicates.ForEach(sub(p) If not CleanList.Any(function(q) p.ID = q.ID And p.Name = q.Name) then CleanList.Add(p))

最后,为了预防未来的需求,您应该将“重复项是什么”定义为一个单独的事物。使用委托非常方便:
Dim AreDuplicates as Func(of T, T, Boolean) = Function(a,b) a.ID = b.ID And a.Name = b.Name
FoundDuplicates = lstOutcomes.Any(Function(p) lstOutcomes.ToArray.Count(Function(q) AreDuplicates(p,q) ) > 1)

1
Dim itemsGroupedByID = lstOutcomes.GroupBy(Function(x) x.ID)
Dim duplicateItems = itemsGroupedByID.Where(Function(x) x.Count > 1) _
                                     .SelectMany(Function(x) x) _
                                     .ToList()

If duplicateItems.Count > 0
    valid = False
    Dim errorMessage = "The following items have a duplicate ID: " & _
                       String.Join(", ", duplicateItems.Select(Function(x) x.Name))
End If

在foreach循环中,你不能使用item.Valid = False的语法。 - undefined
在C#中,您不能更新迭代器的值。我认为在VB中应该是相同的,请尝试一下,应该会引发异常。 - undefined
1
只有在从正在迭代的列表中删除项目时,才会出现问题;设置属性不应该引起任何问题。这段代码已经在LinqPad上经过测试。 - undefined
无论如何,我将代码更改为使用一个本地的“valid”变量。 - undefined
很奇怪,带有一个参数的Where子句应该是可访问的。您能再检查一下,确保您拥有相同的代码吗?我已经在VB 2010中测试过这段代码,没有错误。 - undefined
显示剩余5条评论

1

我会接手Saeed Amiri在C#方面说的内容,并完成它。

        lst.Sort()
        Dim valid As Boolean = true
    dim duplicatedItems = lst.Skip(1) _
        .Where(Function(x,index) x.ID = lst(index).ID)

    Dim count As Integer = duplicatedItems.Count()
    For Each item As objectType In duplicatedItems
        valid = False
        Console.WriteLine("id: " & item.ID & "Name: " & item.Name)
    Next

好的,我编辑了帖子。但是我真的不明白为什么这是必要的。 - undefined
1
这个算法依赖于具有相同ID的项目紧随其后的事实,因此需要按照ID进行排序。与Saeed代码相同的评论,如果没有实现IComparable接口,Sort方法将无法工作。使用OrderBy(Function(x) x.ID)会更简单。 - undefined

1

这个项目已经落后了,我只是像这样匆忙地拼凑在一起:

    ' For each outcome, if it is in the list of valid outcomes more than once, and it is not in the list of 
    ' duplicates, add it to the duplicates list.
    Dim lstDuplicates As New List(Of objectType)
    For Each outcome As objectType In lstOutcomes
        'declare a stable outcome variable
        Dim loutcome As objectType = outcome
        If lstOutcomes.Where(Function(o) o.ID = loutcome.ID).Count > 1 _
        AndAlso Not lstDuplicates.Where(Function(d) d.ID = loutcome.ID).Count > 0 Then
            lstDuplicates.Add(outcome)
        End If
    Next
    If lstDuplicates.Count > 0 Then
        valid = False
        sbErrors.Append("There cannot be multiple outcomes of any kind. The following " & lstDuplicates.Count & _
                        " outcomes are duplicates: ")
        For Each dup As objectType In lstDuplicates
            sbErrors.Append("""" & dup.Name & """" & " ")
        Next
        sbErrors.Append("." & vbNewLine)
    End If

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接