How to add to a list using Linq's aggregate fu

2020-06-15 03:16发布

I have a collection of objects of one type that I'd like to convert to a different type. This can be done easily with foreach, but I'd like to figure out how to use Linq's aggregate function to do it.

The problem is all the Aggregate examples use types line string or int, which support the '+' operator. I'd like to have the accumulator type be a list, which doesn't support '+' semantics.

Here's a quick example:

public class DestinationType
{
    public DestinationType(int A, int B, int C) { ... }
}

var set = from item in context.Items
          select new { item.A, item.B, item.C };

var newSet = set.Aggregate( new List<DestinationType>(),
                            (list, item) => list.Add(new DestinationType(item.A, item.B, item.C)) );

The problem is that List<>.Add returns void. The return type of the second parameter to Aggregate needs to be a List.

If I had a list type that supported '+' type semantics I could just make the second parameter

list + item

However I can't find any collection type that supports this kind of thing.

Seems like this should be easily possible in Linq. Is there a way? Also, if I'm missing an entirely easier way, I'd love to learn about that too. Thanks!

5条回答
我命由我不由天
2楼-- · 2020-06-15 03:45
list.AddRange(context.Items.Select(item => 
  new DestinationType(item.A, item.B, item.C));

I realize it doesn't use the Aggregate function, but you should probably find a better example to use to learn the aggregate.

查看更多
唯我独甜
3楼-- · 2020-06-15 03:47

Assuming this is LINQ to Objects, try...

var newSet = set.Aggregate(new List<DestinationType>(),
                                    (list, item) =>
                                    {
                                        list.Add(new DestinationType(item.A, item.B, item.C));
                                        return list;
                                    });
查看更多
何必那么认真
4楼-- · 2020-06-15 03:50

You can just apply select here.

var newSet = set.Select(item => new DestinationType(...)).ToList();

Aggregate (generally known as fold or reduce) is used to combine elements together where select applies a function to each element.

Ex:

Let f be a unary function, then [a, b, c].select(f) equals [f(a), f(b), f(c)].

Let f be a binary function, then [a, b, c].aggregate(f, init) equals f(a, f(b, f(c, init))).

The way you chose in your example is uncommon in C# but often used in functional programming where (linked) lists are transformed into new lists instead of changing an existing collection:

reversed = fold (\list element -> element:list) [] [1..10]

If you really want to do this computation with aggregate, use Dustin's solution or better implement a linked-list-based type for immutable collections (you can even give this type an operator +).

查看更多
劳资没心,怎么记你
5楼-- · 2020-06-15 03:57

Unless I'm missing something obvious, why not just do:

public class DestinationType
{
    public DestinationType(int A, int B, int C) { ... }
}

var newSet = from item in context.Items
    select new DestinationType(item.A, item.B, item.C);
查看更多
▲ chillily
6楼-- · 2020-06-15 04:01

I think a call to Select combined with ToList() might be what you need here. For example:

context.Items
  .Select(item => new DestinationType(item.A, item.B, item.C))
  .ToList();
查看更多
登录 后发表回答