如何根据列表成员的属性拆分通用List(T)?

时间:2009-11-04 14:02:23

标签: .net vb.net generics .net-2.0

我有一个通用的List(Foo),它包含了Type Foo的n个对象。 Foo的一个属性是PropertyA。 PropertyA可以是ValueA,ValueB或ValueC之一。有没有一种简单的方法可以将它分成三个单独的列表,一个用于ValueA,一个用于ValueB,一个用于ValueC?

我可以编写一些循环原始列表的代码,并根据属性值将每个项目添加到一个新列表中,但这似乎不是很容易维护(如果我突然得到一个ValueD,那该怎么办?)

**编辑。我应该提到我正在使用该框架的2.0版本。

6 个答案:

答案 0 :(得分:5)

在C#中我会写:

  List<List<foo>> result = fooList
    .GroupBy(foo => foo.PropertyA)
    .Select(g => g.ToList())
    .ToList();

答案 1 :(得分:5)

如果你想要valueA,valueB和valueC的3个列表(即使其中一个是空的):

Dim listA = (From x in myList Where x.PropertyA = ValueA).ToList()
Dim listB = (From x in myList Where x.PropertyA = ValueB).ToList()
...

否则,请按照其他人的建议使用GroupBy运算符。


编辑:由于您使用的是Framework 2.0,我猜您将不得不求助于您的循环创意。但是,实现GroupBy的通用算法应该不会太困难。

的内容
Dim dic as New Dictionary(Of TypeOfYourValues, List(Of Foo))
For Each e As Foo In myList
    If Not dic.ContainsKey(e.PropertyA) Then
        dic(e.PropertyA) = New List(Of Foo)
    End if
    dic(e.PropertyA).Add(e)
Next

然后循环遍历字典的值。

答案 2 :(得分:5)

在C#中使用.Net 2.0,我写过(太多次):

 //if PropertyA is not int, change int to whatever that type is
Dictionary<int, List<foo>> myCollections =
  new Dictionary<int, List<foo>>();
//
foreach(Foo myFoo in fooList)
{
  //if I haven't seen this key before, make a new entry
  if (!myCollections.ContainsKey(myFoo.PropertyA))
  {
    myCollections.Add(myFoo.PropertyA, new List<foo>());
  }
  //now add the value to the entry.
  myCollections[myFoo.PropertyA].Add(myFoo);
}
//
// now recollect these lists into the result.
List<List<Foo>> result = new List<List<Foo>>();
foreach(List<Foo> someFoos in myCollections.Values)
{
  result.Add(someFoos);
}

如今,我只想写:

List<List<foo>> result = fooList
  .GroupBy(foo => foo.PropertyA)
  .Select(g => g.ToList())
  .ToList();

或者

 ILookup<TypeOfPropertyA, foo>> result = fooList.ToLookup(foo => foo.PropertyA);

答案 3 :(得分:3)

您可以使用Enumerable.GroupBy

var groupings = list.GroupBy(x => x.PropertyA);

foreach(var grouping in groupings)
{
    // grouping.Key is the grouped value

    foreach(var entry in grouping)
    {
        // process
    }
}

答案 4 :(得分:3)

请参阅下面的C#获取VB.Net版本 - 请注意,由于VB.NET中没有匿名方法,因此还有一个额外的类(FooFinder),所以我需要能够存储匹配状态的东西。

这是一种更“功能”的方式来完成同样的事情,但仍然使用C#2.0语法。请注意,与其他解决方案(循环/字典)的重要区别在于使用List上的FindAll方法,它将迭代您的集合并返回委托返回true的所有项目。 C#:

using System;
using System.Collections.Generic;

namespace SplitList
{
    class Program
    {
        class Foo
        {
            public Foo(string propertyA, int number)
            {
                _propertyA = propertyA;
                _number = number;
            }

            private int _number;

            private string _propertyA;

            public string PropertyA
            {
                get { return _propertyA; }
            }

            public int Number
            {
                get { return _number; }
            }
        }
        static void Main(string[] args)
        {
            List<Foo> foos = new List<Foo>();
            foos.Add(new Foo("ValueA", 1));
            foos.Add(new Foo("ValueA", 2));
            foos.Add(new Foo("ValueA", 3));
            foos.Add(new Foo("ValueA", 4));
            foos.Add(new Foo("ValueB", 5));
            foos.Add(new Foo("ValueB", 6));
            foos.Add(new Foo("ValueC", 7));
            foos.Add(new Foo("ValueC", 8));
            foos.Add(new Foo("ValueC", 9));

            List<Foo> aFoos = foos.FindAll(delegate(Foo f) { return f.PropertyA == "ValueA"; });
            List<Foo> bFoos = foos.FindAll(delegate(Foo f) { return f.PropertyA == "ValueB"; });
            List<Foo> cFoos = foos.FindAll(delegate(Foo f) { return f.PropertyA == "ValueC"; });
            WriteFoos("ValueA", aFoos);
            WriteFoos("ValueB", bFoos);
            WriteFoos("ValueC", cFoos);
            Console.ReadLine();
        }

        private static void WriteFoos(string propertyAValue, List<Foo> list)
        {
            Console.WriteLine("Group {0}:", propertyAValue);
            list.ForEach(delegate(Foo f)
                             {
                             Console.WriteLine("Number:{0}, PropertyA:{1}", f.Number, f.PropertyA);
                             });

        }
    }
}

VB.NET:

Module Module1

    Class FooFinder
        Public Sub New(ByVal propertyAValue As String)
            Me.PropertyAValue = propertyAValue
        End Sub
        Public ReadOnly PropertyAValue As String
        Function Matches(ByVal f As Foo) As Boolean
            Return (f.PropertyAValue = Me.PropertyAValue)
        End Function
    End Class
    Class Foo

        Public Sub New(ByVal propertyAValue As String, ByVal number As Integer)
            _propertyAValue = propertyAValue
            _number = number
        End Sub

        Private _propertyAValue As String
        Private _number As Integer

        Public Property PropertyAValue() As String
            Get
                Return _propertyAValue
            End Get
            Set(ByVal value As String)
                _propertyAValue = value
            End Set
        End Property

        Public Property Number() As Integer
            Get
                Return _number
            End Get
            Set(ByVal value As Integer)
                _number = value
            End Set
        End Property
    End Class
    Sub Main()

        Dim foos As New List(Of Foo)
        foos.Add(New Foo("ValueA", 1))
        foos.Add(New Foo("ValueA", 2))
        foos.Add(New Foo("ValueA", 3))
        foos.Add(New Foo("ValueB", 4))
        foos.Add(New Foo("ValueB", 5))
        foos.Add(New Foo("ValueC", 6))
        foos.Add(New Foo("ValueC", 7))
        foos.Add(New Foo("ValueC", 8))
        foos.Add(New Foo("ValueC", 9))

        Dim aFoos As List(Of Foo) = foos.FindAll(AddressOf New FooFinder("ValueA").Matches)
        Dim bFoos As List(Of Foo) = foos.FindAll(AddressOf New FooFinder("ValueB").Matches)
        Dim cFoos As List(Of Foo) = foos.FindAll(AddressOf New FooFinder("ValueC").Matches)

        WriteFoos("ValueA", aFoos)
        WriteFoos("ValueB", bFoos)
        WriteFoos("ValueC", cFoos)
        Console.ReadLine()


    End Sub

    Private Sub WriteFoos(ByVal propertyAValue As String, ByVal list As List(Of Foo))
        Console.WriteLine("PropertyAValue:{0}", propertyAValue)
        For Each f As Foo In list
            Console.WriteLine("Number:{0}, PropertyAValue:{1}", f.Number, f.PropertyAValue)
        Next
    End Sub
End Module

答案 5 :(得分:1)

var query = from foo in list
            group foo by foo.PropertyA;

List<Foo> valueAGroup = query.First(g => g.Key == ValueA).ToList();
List<Foo> valueBGroup = query.First(g => g.Key == ValueB).ToList();
List<Foo> valueCGroup = query.First(g => g.Key == ValueC).ToList();

如果ToList()足够好,您可以省略IEnumerable<Foo>次来电。

如果ValueX可能没有PropertyA等于ValueX的项,那么First将抛出异常。在这种情况下,最好这样做:

List<Foo> valueXGroup = (query.FirstOrDefault(g => g.Key == ValueX) ??
    Enumerable.Empty<Foo>()).ToList();

这将为您提供一个空列表,而不是抛出异常。