使用LINQ,您将如何从列表中过滤出特定条件的所有项目,仅保留一个项目

本文关键字:项目 保留 一个 LINQ 使用 过滤 列表 特定条件 | 更新日期: 2023-09-27 18:15:03

我意识到我的标题可能不是很清楚所以这里有一个例子:

我有一个对象列表,它有两个属性,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 },
}

使用LINQ,将所有A = 2项折叠成第一个A = 2项并与所有其他项一起返回的最优雅的方法是什么?这将是预期的结果。

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 },
}

我不是一个LINQ专家,已经有一个"手动"的解决方案,但我真的很喜欢LINQ的表现力,并很好奇,看看它是否可以做得更好。

使用LINQ,您将如何从列表中过滤出特定条件的所有项目,仅保留一个项目

如何:

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

我们的想法是首先通过A对它们进行分组,然后再次选择它们(用.SelectMany将其扁平化),但在Key是我们想要崩溃的情况下,我们只需使用Take(1)的第一个条目。

您可以使用GroupBy来完成此任务。按A对项目进行分组,并使用SelectMany将每组再次投影到平面列表中。在SelectMany中,检查A是否为2,如果为Take(1),否则返回该组的所有结果。我们使用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;
});

一个可能的解决方案(如果您坚持使用LINQ):

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

将所有具有A=2的项目分组到具有等于2的键的组中,但所有其他项目将具有唯一的组键(新guid),因此您将有许多拥有一个项目的组。然后从每组中取第一件。

另一种方式:

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

基于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;
});

这个呢:

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

如果你想要更有效的方式,它将是:

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