【问题标题】:Filtering IEnumerable by collection (Linq)按集合过滤 IEnumerable (Linq)
【发布时间】:2014-06-21 06:28:41
【问题描述】:

我想通过它正在收集的任何对象的特定属性来过滤 IEnumerable 对象。我希望选择按一个或多个属性值进行过滤只有在运行时才能知道要过滤的值(以及哪些值)。

好的,举个例子,收集的对象可以是以下结构:

public struct Person
{
    public string Name { get; set; }
    public string Profession{ get; set; }
}

这个结构可以被下面的列表使用,我已经填充了一些任意值:

List<Person> people= new List<Person>;
people.Add(new Person(){Name = "Mickey", Profession="Tinker"};
people.Add(new Person(){Name = "Donald", Profession="Tailor"};
people.Add(new Person(){Name = "Goofy", Profession="Soldier"};
people.Add(new Person(){Name = "Pluto", Profession="Spy"};

然后将其放入一个 IEnumerable 中(所有这些都首先转移到它)

var wantedPeople = from n in this.people select n;

假设用户只对“裁缝”和“间谍”职业感兴趣,并且通过某种 gui 诡计创建了以下集合:

List<string> wantedProfessions = new List<string>();
wantedProfessions.Add("Tailor");
wantedProfessions.Add("Spy");

现在我可以使用什么 Linq 语句来归档我的 WantedPeople,以便它只包含裁缝和间谍条目? 我知道我可以使用 where 子句,但我不知道如何定制它以获得我想要的(并且执行以下操作 不是我想要的,因为它仅适用于上面的 WantedProfessions 集合(例如,此集合将在运行时更改):

wantedPeople = from n in wantedPeople
              where n.Profession == wantedProffessions[0] || n.Profession == wantedProffessions[1]
              select n;

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    如果您想从给定列表中查看任何想要的职业:

    wantedPeople = from n in wantedPeople
                   where wantedProffessions.Contains(n.Profession)
                   select n;
    

    或者您可以通过一个一个地应用过滤器来使用 lambda 语法构建查询:

    var query = people.AsEnumerable();
    
    if (!String.IsNullOrEmpty(name))
        query = query.Where(p => p.Name == name);
    
    if (wantedProfessions.Any())
        query = query.Where(p => wantedProfessions.Contains(p.Profession));
    

    如果你想创建更复杂的过滤器,比如一些名字和几个职业,你可以使用Specification pattern。规范可以通过这个简单的接口来定义:

    public interface ISpecification<T>
    {
       bool Satisfied(T entity);
    }
    

    它只是检查给定的实体(人)是否满足规范。规格看起来也很简单:

    public class PersonNameSpecification : ISpecification<Person>
    {
        private string _name;
        public PersonNameSpecification(string name)
        {
            _name = name;
        }
    
        public bool Satisfied(Person person)
        {
            return person.Name == _name;
        }
    }
    

    职业规范:

    public class PersonProfessionSpecification : ISpecification<Person>
    {
        private string[] _professions;
        public PersonProfessionSpecification(params string[] professions)
        {
            _professions = professions;
        }
        public bool Satisfied(Person person)
        {
            return _professions.Contains(person.Profession);
        }
    }
    

    您可以创建实现布尔逻辑的规范,例如 OrSpecification 或 AndSpecification:

    public class AndSpecification<T> : ISpecification<T>
    {
        private ISpecification<T> _specA;
        private ISpecification<T> _specB;
        public AndSpecification(ISpecification<T> specA, ISpecification<T> specB)
        {
            _specA = specA;
            _specB = specB;
        }
        public bool Satisfied(T entity)
        {
            return _specA.Satisfied(entity) && _specB.Satisfied(entity);
        }
    }
    
    public static class SpecificationExtensions
    {
        public static ISpecification<T> And<T>(
           this ISpecification<T> specA, ISpecification<T> specB)
        {
            return new AndSpecification<T>(specA, specB);
        }
    }
    

    现在您可以创建复杂的规范来描述您想要获得的人:

    var professionSpec = new PersonProfessionSpecification("Tailor", "Spy");
    var nameSpec = new PersonNameSpecification("Pluto");
    var spec = professionSpec.And(nameSpec);
    

    并获得所需的人员:

    var result = people.Where(spec.Satisfied);
    

    【讨论】:

    • 感谢 Sergey 并为长时间的延误表示歉意。
    【解决方案2】:

    Sergey B 的解决方案适合您的示例。

    假设您没有使用具有 Contains() 方法的集合,您还可以执行以下操作:

    var wantedPeople = from n in people
                       from p in wantedProffessions
                       where n.Profession.Equals(p)
                       select n;
    

    【讨论】:

    • 实际上,在这种情况下我会选择join - 它在内部创建查找比枚举两个集合 O(n*m) 更有效
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-26
    • 1970-01-01
    • 1970-01-01
    • 2011-04-21
    • 2012-04-08
    相关资源
    最近更新 更多