【问题标题】:LINQ VB how to check for duplicates in a list of objectsLINQ VB如何检查对象列表中的重复项
【发布时间】:2011-10-11 20:33:04
【问题描述】:

我有一个对象列表,每个对象都有 2 个相关属性:“ID”和“Name”。让我们将列表称为“lstOutcomes”。 如果至少有一个重复项,我需要检查列表中的重复项(意思是 object1.ID = object2.ID 等)并设置一个标志(valid = false 或其他内容)。此外,如果失败,最好向用户发送一条消息,提及对象的“名称”。

我确信我需要使用 Group By 运算符来执行此操作,但我不习惯在 LINQ 中这样做,并且那里的示例对我没有帮助。 This article 似乎接近我需要的,但不完全是,它在 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

帮助?

【问题讨论】:

标签: vb.net linq group-by


【解决方案1】:

我会用 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 时才有效。
  • 此外,“duplicatedItems”将不包含列表中的第一个重复项,仅包含具有相同 ID 的下一个重复项(尽管这可能没问题)。
  • @Meta-Knight,是的,但我假设第一个数字是有效的,之后任何其他相同的数字都是无效的,并且只是询问是否存在重复项目的问题。如果我可以使用索引Any,我会使用它。
【解决方案2】:
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
  • 为什么我不能这样做?目标是将对象上的 Valid 标志设置为 false,假设存在这样的标志。
  • 在C#中,不能更新迭代器的值,我认为在VB中应该是一样的,试试吧,应该会抛出异常。
  • 如果我从正在迭代的列表中删除了一个项目,这只会是一个问题,设置属性不应该导致任何问题。这段代码已经用 LinqPad 测试过了。
  • 无论如何,我将代码更改为使用本地“有效”变量。
【解决方案3】:

我会收回 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

【讨论】:

  • 好的,我编辑了帖子。但我真的不明白为什么有必要
  • 该算法依赖于具有相同ID的项目相互跟随的事实,因此需要按ID排序。与 Saeed 的代码相同的注释,如果未实现 IComparable,Sort 方法将不起作用。使用OrderBy(Function(x) x.ID) 会更简单。
【解决方案4】:

项目落后了,我只是这样破解它:

    ' 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

【讨论】:

    【解决方案5】:

    虽然晚了,但可以帮到别人。

    你可以用一对非常干净的单眼线来实现这一点:

    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)
    

    【讨论】:

      猜你喜欢
      • 2013-04-18
      • 1970-01-01
      • 1970-01-01
      • 2020-03-30
      • 2022-01-07
      • 1970-01-01
      • 2020-12-09
      • 2011-09-21
      • 1970-01-01
      相关资源
      最近更新 更多