从列表中只选择一种类型

本文关键字:一种 类型 列表 选择 | 更新日期: 2023-09-27 18:21:36

我有一个类似的类

class Person {
 private Id;
 private parentId;
 private Name;
}

我有一份名单,看起来像这样:

List<Person> persons = new List<Person>();
persons.Add(new Person{Id =1, Name = "Andy", parentId = 0});
persons.Add(new Person{Id =2, Name = "Sandy", parentId = 0});
persons.Add(new Person{Id =3, Name = "Carter", parentId = 9});
persons.Add(new Person{Id =4, Name = "Mark", parentId = 9});
persons.Add(new Person{Id =5, Name = "Martin", parentId = 99});
persons.Add(new Person{Id =6, Name = "Matt", parentId = 99});

我想要的是:

persons.Add(new Person{Id =1, Name = "Andy", parentId = 0});
persons.Add(new Person{Id =2, Name = "Sandy", parentId = 0});
persons.Add(new Person{Id =3, Name = "Carter", parentId = 9});
persons.Add(new Person{Id =6, Name = "Matt", parentId = 99});

我想保留parentId为0的所有人,但只从多个分组对象中选择一个。

感谢

从列表中只选择一种类型

这应该可以完成任务(从测试来看)。

var rootItems = persons.Where(x=>x.parentId==0);
var childItems = persons.Where(x=>x.parentId!=0).GroupBy(x=>x.parentId).Select(x=>x.First());
var requiredItems = rootItems.Concat(childItems);

它基本上分为两部分,第一部分简单地获取所有根项。第二个按parentId对它们进行分组,然后选择每个组中的第一个项目。如果你想进一步完善选择哪一个,可以对其进行调整。

附加说明:

作为产生问题确切输出的练习,以下内容可用于childItems:

var childItems = persons.Where(x=>x.parentId!=0)
    .GroupBy(x=>x.parentId)
    .Select(
        x=>x.OrderBy(y=>y.Id%3)
        .First()
);

这显然是一种从选项中进行选择的略显荒谬的方式,但它确实展示了如何通过添加某种排序标准来更改选择的项目。

要从parentId的每个分组中获得一个人,只需根据该值分组,然后从每个分组中选择第一个人。然后,您可以将其与父id为0的人连接起来。

var query = people.Where(p => p.parentId == 0)
    .Concat(people.Where(p => p.parentId != 0)
        .GroupBy(p => p.parentId)
        .Select(group => group.First()));

假设您有一个AllPersons:列表

var parentsAdded = new HashSet<int>();
foreach(var person in AllPersons)
{
    if(person.parentId == 0 || parentsAdded.Add(person.parentId))
    {
       persons.Add(person);
    }
}

最简单的方法是对使用Concat方法合并的组中的parentId == 0使用两个单独的查询。

var results = people.Where(x => x.parentId == 0)
                    .Concat(persons.Where(x => x.parentId != 0)
                                   .GroupBy(x => x.parentId)
                                   .Select(x => x.First()))

然而,它需要两次通过集合。我建议您编写自己的扩展方法:

public static class Enumerable
{
    public static IEnumerable<Person> GetFirsts(this IEnumerable<Person> source)
    {
        var set = new HashSet<int>();
        foreach (var person in source)
        {
            if (person.parentId == 0 || set.Add(person.parentId))
                yield return person;
        }
    }
}

然后像这样使用:

var results = people.GetFirsts();

编辑

GetFirsts:的通用版本

public static IEnumerable<TSource> GetFirsts<TSource, TKey> (this IEnumerable<TSource> source,
                                                             Func<TSource, TKey> selector,
                                                             TKey zeroValue)
{
    var set = new HashSet<TKey>();
    foreach (var item in source)
    {
        if (selector(item).Equals(zeroValue) || set.Add(selector(item)))
            yield return item;
    }
}