Удаление повторяющихся элементов из многоколоночного списка - PullRequest
1 голос
/ 30 августа 2010

Ответ на вопрос

Спасибо, Дэн!Ваш код работал отлично, и вы спасли мне жизнь сегодня!Большое количество интернетов, добрый сэр.

Оригинал

Я был щедро направлен сообществом на использование LINQ для поиска дубликатов в моих списках в последний раз.Однако сейчас я нахожусь в затруднительном положении, потому что мне нужно найти и удалить дубликаты из многоколоночного списка.Я пытался использовать LINQ, но он говорит, что объект listview не "запрашиваемый".Могу ли я найти и удалить дубликаты, используя только один столбец списка?

Спасибо

ОБНОВЛЕНИЕ

Private Shared Sub RemoveDuplicateListViewItems(ByVal listView As ListView)
    Dim duplicates = listView.Items.Cast(Of ListViewItem)() _
    .GroupBy(Function(item) item.Text)
    .Where(Function(g) g.CountAtLeast(2))
    .SelectMany(Function(g) g)

    For Each duplicate As ListViewItem In duplicates
        listView.Items.RemoveByKey(duplicate.Name)
    Next
End Sub

Эточто у меня до сих пор благодаря Дэну.По-прежнему возникают ошибки в строке «Dim дубликаты».

ОБНОВЛЕНИЕ 2 Ниже приведен код модуля и функции в форме:

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 RemoveDuplicateListViewItems(ByVal listView As ListView)
    Dim duplicates = listView.Items.Cast(Of ListViewItem)() _
        .GroupBy(Function(item) item.Text) _
        .Where(Function(g) g.CountAtLeast(2)) _
        .SelectMany(Function(g) g)

    For Each duplicate As ListViewItem In duplicates
        listView.Items.RemoveByKey(duplicate.Name)
    Next
End Sub

Кодтеперь работает нормально, когда я это называю.Но он не удаляет дубликаты:

Пример дубликата

Может быть, с этим снимком экрана вы можете увидеть, что я собираюсь здесь.Большое спасибо за то, что вы так терпеливы со мной!

1 Ответ

1 голос
/ 30 августа 2010

Ну, вам понадобится какой-то метод для определения, являются ли два ListViewItem объекта дубликатами.

Как только это будет сделано, реализация будет довольно простой.

Допустим, вы хотитесчитать два элемента одинаковыми, если текст в первом столбце одинаков (например).Затем вы можете написать быструю реализацию IEqualityComparer<ListViewItem>, такую ​​как:

class ListViewItemComparer : IEqualityComparer<ListViewItem>
{
    public bool Equals(ListViewItem x, ListViewItem y)
    {
        return x.Text == y.Text;
    }

    public int GetHashCode(ListViewItem obj)
    {
        return obj.Text.GetHashCode();
    }
}

Затем вы можете удалить дубликаты, например:

static void RemoveDuplicateListViewItems(ListView listView)
{
    var uniqueItems = new HashSet<ListViewItem>(new ListViewItemComparer());

    for (int i = listView.Count - 1; i >= 0; --i)
    {
        // An item will only be added to the HashSet<ListViewItem> if an equivalent
        // item is not already contained within. So a return value of false indicates
        // a duplicate.
        if (!uniqueItems.Add(listView.Items[i]))
        {
            listView.Items.RemoveAt(i);
        }
    }
}

ОБНОВЛЕНИЕ : Приведенный выше код удаляет дубликаты любых элементов, которые появляются в ListView более одного раза;то есть он оставляет один экземпляр каждого.Если вы хотите на самом деле удалить все экземпляров любых элементов, которые появляются более одного раза, подход немного отличается.

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

public static bool CountAtLeast<T>(this IEnumerable<T> source, int minimumCount)
{
    int count = 0;
    foreach (T item in source)
    {
        if ((++count) >= minimumCount)
        {
            return true;
        }
    }

    return false;
}

Затем найдите дубликаты следующим образом:

static void RemoveDuplicateListViewItems(ListView listView)
{
    var duplicates = listView.Items.Cast<ListViewItem>()
        .GroupBy(item => item.Text)
        .Where(g => g.CountAtLeast(2))
        .SelectMany(g => g);

    foreach (ListViewItem duplicate in duplicates)
    {
        listView.Items.RemoveByKey(duplicate.Name);
    }
}

ОБНОВЛЕНИЕ 2 : Звучит так, как будто вы 'мы уже смогли преобразовать большинство из вышеперечисленного в VB.NET.Строка, которая доставляет вам неприятности, может быть записана следующим образом:

' Make sure you have Option Infer On. '
Dim duplicates = listView.Items.Cast(Of ListViewItem)() _
    .GroupBy(Function(item) item.Text) _
    .Where(Function(g) g.CountAtLeast(2)) _
    .SelectMany(Function(g) g)

Кроме того, если у вас есть какие-либо проблемы с использованием метода CountAtLeast описанным выше способом, вам необходимо использовать ExtensionAttribute класс для написания методов расширения в VB.NET:

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
...