Listview,使用LINQ删除重复项并基于subitem比较

时间:2014-02-18 01:54:08

标签: .net vb.net linq visual-studio listview

我想根据子项索引从ListViewItemCollection删除重复项。

使用LINQ方法非常重要。

这就是我没有运气的尝试:

Private Function RemoveListViewDuplicates(ByVal Items As ListView.ListViewItemCollection,
                                          ByVal CompareColumn As Integer) As ListView.ListViewItemCollection

    Dim Deduplicated =
        (From Item As ListViewItem In Items
         Group By Item.SubItems(CompareColumn).Text Into Distinct()) '.ToArray
    ' At this point the good thing is that the '.Distinct' grouped member of the 
    ' resulting 'Deduplicated' object contains only the non-duplicated ListviewItems
    ' but, I can't find the way to return that single member '.Distinct' as a 'ListView.ListViewItemCollection'
    ' instead of returning both '.Text' and '.Distinct' members.
    '
    ' I just want to return the 'Items' object that I pass to the function but without duplicates,
    ' and in the same return-type of the object that I've passed to this function 
    ' (ListView.ListViewItemCollection) as you can understand.

    Return Deduplicated

End Function

然后我应该这样做:

    Dim items As ListView.ListViewItemCollection =
        RemoveListViewDuplicates(ListView1.Items, 0)

    For Each Item As ListViewItem In items
        MsgBox(Item.Text)
    Next Item
  

更新

我正在尝试解决方案here,但稍加修改以保留集合上的1个重复项目,我遇到的问题是它直接在ListView控件上运行tehn不是我想要的,我想处理一个对象,该对象存储listview控件的当前项,但在删除重复项时不会直接影响控件...

这是方法:

Private Function RemoveListViewDuplicates(ByVal Items As ListView.ListViewItemCollection,
                                          ByVal SubitemCompare As Integer) As ListView.ListViewItemCollection

    Dim Duplicates As ListViewItem() =
        Items.Cast(Of ListViewItem)().
        GroupBy(Function(Item As ListViewItem) Item.SubItems(SubitemCompare).Text).
        Where(Function(g As IGrouping(Of String, ListViewItem)) g.Count <> 1).
        SelectMany(Function(g As IGrouping(Of String, ListViewItem)) g).
        Skip(1).
        ToArray()

    ' This removes the items from the listview control, directlly
    For Each Item As ListViewItem In Duplicates
        Items.Remove(Item)
    Next Item

    Return Items

End Function

所需用法:

    Dim DuplicatedItems As ListView.ListViewItemCollection =
        New ListView.ListViewItemCollection(ListView1)

    Dim DeDuplicatedItems As ListView.ListViewItemCollection =
        RemoveListViewDuplicates(DuplicatedItems, 0)

    ' I add the Items without duplicates on another Listview, 
    ' preserving the duplicates on the original Listview1 control.
    ListView2.Items.AddRange(DeDuplicatedItems)

2 个答案:

答案 0 :(得分:1)

尝试类似下面的例子:

Dim StringList As List(Of String) = ...
StringList = StringList.Distinct().ToList()

更新

试试下面的内容。请记住,我习惯于编写C#,所以我可能会留下一些C#。

Option Infer On
Imports System.Linq
Imports System.Runtime.CompilerServices

Module CountAtLeastExtension

    <Extension()> _
    Public Function CountAtLeast(Of T)(ByVal source As IEnumerable(Of T), ByVal minimumCount As Integer) As Boolean
        Dim count = 0
        For Each item In source
             count += 1
            If count >= minimumCount Then
                Return True
            End If
        Next

        Return False
    End Function

End Module

Private Shared Sub RemoveListViewDuplicates(ByVal listView As ListView)

    Dim duplicates = listView.Items.Cast(Of ListViewItem.SubItems)() _
        .GroupBy(Function(item) item.Text) _
        .Where(Function(g) g.CountAtLeast(2)) _
        .SelectMany(Function(g) g)

    For Each duplicate As ListViewItem In duplicates
        listView.SubItems.RemoveByKey(duplicate.Name)
    Next

End Sub

Private Shared Sub RemoveDuplicateListViewItems(ByVal ListView As ListView.SubItems)

    Dim uniqueItems = New HashSet(Of ListViewItem)

    For Each item As String In ListView

        If !uniqueItems.Add(ListView.Items) Then
            ListView.SubItems.RemoveAt(item)
        End If

    Next
End Sub

答案 1 :(得分:1)

我认为你的方法有2个基本问题。首先,ListViewItems是对象。当它们从ListViewItemCollection中删除时,2个属性会更改:索引(转到0),ListView(所有者)属性变为Nothing。因此,当您删除Collection2中的ListViewItem时,将从主/原始集合中删除,因为它们是相同的LVI对象

链接到(并在上面复制)的其他SO答案不是一个合适的起点,因为确实想要从您不这样做的源中删除。 解决方案:如果你想要2个具有不同LVI项目的集合(1 == w / dupes; 2 ==没有欺骗),你必须克隆项目 - 要么第一个集合中的所有项目都要从DupesCollection开始或克隆Dupes。我不知道除了循环之外你会怎么做(即通过LINQ)。

如果您在开始时克隆整个集合,您应该可以按照自己的意愿继续沿LINQ路径前进,只需确保您正在处理克隆的集合。这似乎是昂贵的,只是为了能够使用LINQ(尾巴摇尾巴)。

其次,ListViewItemCollection需要LV所有者。创建其他集合时,您将其分配给特定的LV:

Dim DuplicatedItems As ListView.ListViewItemCollection =
    New ListView.ListViewItemCollection(ListView1)

新的集合无法重新分配给不同的LV(看起来你想在不同的LV中显示Dupes)。您无法更改所有者,也无法直接替换Items。您应该能够在开始时将其分配到所需的目标LV,但必须克隆项目。否则,将Dupe Items集合移动到LV2的唯一方法是循环播放。

我不确定分配给单个LV的2个LVI集合会发生什么,但是当你真正需要的是重复指数时,制作新集合似乎很昂贵:

   ' get a list of indices comparing the Text for SubItem(index)
    Public Function DuplicateItemIndices(index As Integer) As List(Of Integer)
        Dim lvDupes As New List(Of Integer)

        If Items.Count = 0 Then Return Nothing
        If Items(0).SubItems.Count < index Then Return Nothing

        Dim ndx As Integer

        For Each lvi As ListViewItem In Items
            ' already caught, keep going
            If lvDupes.Contains(lvi.Index) Then Continue For

            ndx = FindDupeItem(lvi, index)
            ' not sure the second condition can happen here
            If ndx > 0 AndAlso lvDupes.Contains(ndx) = False Then
                lvDupes.Add(ndx)
            End If
        Next

        Return lvDupes

    End Function

    ' returns an index for an item matching this one
    Private Function FindDupeItem(lvi As ListViewItem, 
                                  index As Integer) As Integer

        For Each lvx As ListViewItem In Items
            If lvx Is lvi Then Continue For

            If lvx.SubItems(index).Text = lvi.SubItems(index).Text Then
                Return lvx.Index
            End If
        Next
        ' signal for no match
        Return -1

    End Function

根据我理解您的目的,使用主列表中返回的List(of DuplicateItemIndices)克隆项目并隐藏在DuplicatesLV中。