简体   繁体   English

使用LINQ,您如何从列表中筛选出除特定条件之外的所有项目?

[英]Using LINQ, how would you filter out all but one item of a particular criteria from a list?

I realize my title probably isn't very clear so here's an example: 我意识到我的标题可能不是很清楚所以这是一个例子:

I have a list of objects with two properties, A and B. 我有一个具有两个属性A和B的对象列表。

public class Item
{
    public int A { get; set; }
    public int B { get; set; }
}

var list = new List<Item>
{
    new Item() { A = 0, B = 0 },
    new Item() { A = 0, B = 1 },
    new Item() { A = 1, B = 0 },
    new Item() { A = 2, B = 0 },
    new Item() { A = 2, B = 1 },
    new Item() { A = 2, B = 2 },
    new Item() { A = 3, B = 0 },
    new Item() { A = 3, B = 1 },
}

Using LINQ, what's the most elegant way to collapse all the A = 2 items into the first A = 2 item and return along with all the other items? 使用LINQ,将所有A = 2项目折叠成第一个A = 2项目并返回所有其他项目的最优雅方法是什么? This would be the expected result. 这将是预期的结果。

var list = new List<Item>
{
    new Item() { A = 0, B = 0 },
    new Item() { A = 0, B = 1 },
    new Item() { A = 1, B = 0 },
    new Item() { A = 2, B = 0 },
    new Item() { A = 3, B = 0 },
    new Item() { A = 3, B = 1 },
}

I'm not a LINQ expert and already have a "manual" solution but I really like the expressiveness of LINQ and was curious to see if it could be done better. 我不是LINQ专家,已经有一个“手动”解决方案,但我真的很喜欢LINQ的表现力,并且很想知道它是否可以做得更好。

How about: 怎么样:

var collapsed = list.GroupBy(i => i.A)
                    .SelectMany(g => g.Key == 2 ? g.Take(1) : g);

The idea is to first group them by A and then select those again (flattening it with .SelectMany ) but in the case of the Key being the one we want to collapse, we just take the first entry with Take(1) . 我们的想法是首先用A对它们进行分组,然后再次选择它们(用.SelectMany平它)但是在Key是我们想要折叠的那个的情况下,我们只用Take(1)取第一个条目。

One way you can accomplish this is with GroupBy . 您可以通过GroupBy实现此目的。 Group the items by A , and use a SelectMany to project each group into a flat list again. A分组项目,并使用SelectMany将每个组再次投影到一个平面列表中。 In the SelectMany , check if A is 2 and if so Take(1) , otherwise return all results for that group. SelectMany ,检查A是否为2 ,如果是则Take(1) ,否则返回该组的所有结果。 We're using Take instead of First because the result has to be IEnumerable . 我们使用Take而不是First因为结果必须是IEnumerable

var grouped = list.GroupBy(g => g.A);
var collapsed = grouped.SelectMany(g =>
{
     if (g.Key == 2)
     {
         return g.Take(1);
     }
     return g;
});

One possible solution (if you insist on LINQ): 一种可能的解决方案(如果你坚持使用LINQ):

int a = 2;  
var output = list.GroupBy(o => o.A == a ? a.ToString() : Guid.NewGuid().ToString())
                 .Select(g => g.First())
                 .ToList();

Group all items with A=2 into group with key equal to 2 , but all other items will have unique group key (new guid), so you will have many groups having one item. A=2所有项目分组,密钥等于2 ,但所有其他项目将具有唯一的组密钥(新guid),因此您将拥有许多具有一个项目的组。 Then from each group we take first item. 然后从每个组我们采取第一项。

Yet another way: 另一种方式:

var newlist = list.Where (l => l.A != 2 ).ToList();
newlist.Add( list.First (l => l.A == 2) );

An alternative to other answers based on GroupBy can be Aggregate : 基于GroupBy其他答案的替代方法可以是Aggregate

// Aggregate lets iterate a sequence and accumulate a result (the first arg)
var list2 = list.Aggregate(new List<Item>(), (result, next) => {
     // This will add the item in the source sequence either
     // if A != 2 or, if it's A == 2, it will check that there's no A == 2
     // already in the resulting sequence!
     if(next.A != 2 || !result.Any(item => item.A == 2)) result.Add(next);  
     return result;
});

What about this: 那这个呢:

list.RemoveAll(l => l.A == 2 && l != list.FirstOrDefault(i => i.A == 2));

if you whould like more efficient way it would be: 如果你想要更有效的方式,那将是:

var first = list.FirstOrDefault(i => i.A == 2);
list.RemoveAll(l => l.A == 2 && l != first);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM