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

发布于 2024-08-10 14:01:31 字数 282 浏览 1 评论 0原文

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

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

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

I have a generic List (of Foo) which contains n objects of Type Foo. One of the properties of Foo is PropertyA. PropertyA can be one of ValueA, ValueB or ValueC. Is there an easy way of splitting this into three seperate Lists, one for ValueA, one for ValueB and one for ValueC?

I can write some code which loops the original list and adds each item to a new list based on the property value but this does not seem to be very maintainable (what if I suddenly get a ValueD for example?)

**EDIT. I should have mentioned that I'm using version 2.0 of the framework.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

李白 2024-08-17 14:01:31

在带有 .Net 2.0 的 C# 中,我已经写了(太多次):

 //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);

In C# with .Net 2.0, I have written (too many times):

 //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);
}

Nowadays, I just write:

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

Or

 ILookup<TypeOfPropertyA, foo>> result = fooList.ToLookup(foo => foo.PropertyA);
毅然前行 2024-08-17 14:01:31

在 C# 中我会这样写:

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

In C# I would write:

  List<List<foo>> result = fooList
    .GroupBy(foo => foo.PropertyA)
    .Select(g => g.ToList())
    .ToList();
你的呼吸 2024-08-17 14:01:31

如果您想要 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

then 循环字典的值。

If you want exactly 3 lists for valueA, valueB and valueC (even if one of them is empty):

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

Otherwise, use the GroupBy operator as suggested by others.


EDIT: Since you are using Framework 2.0, I guess you'll have to resort to your loop idea. A generic algorithm implementing GroupBy shouldn't be too difficult, though. Something along the lines of

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

Then loop through the values of the dictionary.

笨死的猪 2024-08-17 14:01:31

您可以使用 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
    }
}

You can use 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
    }
}
小…红帽 2024-08-17 14:01:31

请参阅下面的 VB.Net 版本的 C# - 请注意,由于 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

See below the C# for the VB.Net version - note that there's one additional class (FooFinder) since there are no anonymous methods in VB.NET, so I needed something to be able to store the match state.

Here's a more "functional" way to accomplish the same thing, but still using C# 2.0 syntax. Note the important difference from other solutions (looping/dictionaries) is the use of the FindAll method on List, which will iterate over your collection and return all items for which the delegate returns 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
她比我温柔 2024-08-17 14:01:31
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();

或者,如果 IEnumerable 足够好,您可以省略 ToList() 调用。

如果对于 ValueX 来说,可能不存在 PropertyA 等于 ValueX 的项目,则 First 将引发异常。在这种情况下,最好这样做:

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

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

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();

Or you could leave out the ToList() calls if an IEnumerable<Foo> is good enough.

If it is possible that for a ValueX there are no items for which PropertyA equals ValueX, then First will throw an exception. In that case, it's a good idea to do this:

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

This will give you an empty list instead of throwing an exception.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文