使用 LINQ 缩小结果集范围

发布于 2024-11-07 02:07:25 字数 568 浏览 1 评论 0原文

假设我有一个名为 GetCatsByColor 的方法,它将颜色作为字符串,一个名为 GetCatsByName 的方法,它将名称作为字符串,以及 GetCatsByBirthDate 其中使用两个 DateTime 作为时间范围。

现在假设我有一个 CatFilter 类,其中包含一个 List 名称、List 颜色和两个 DateTime ,表示时间跨度的“开始”日期和“结束”日期。我想要做的是创建一个 GetFilteredCats 方法,该方法采用这些 Filter 对象之一并返回满足给定 Filter< 规范的 Cats 集合/代码>。

我很难想出一种有效的方法来获得所需的结果,最好使用 LINQ/lambda 表达式。

进行这种连接的最佳方法是什么?我应该考虑哪些扩展方法?在 foreach 循环中修改集合通常是不可取/不可能的,那么我的策略应该是什么?

Say I have a method called GetCatsByColor which takes a color as a string, a method GetCatsByName which takes a name as a string, and GetCatsByBirthDate which takes two DateTimes acting as a range of time.

Now say I have a CatFilter class which holds a List of names, List of colors, and two DateTimes, representing the "from" date and the "to" date of a timespan. What I am trying to do is create a GetFilteredCats method that takes one of these Filter objects and returns a collection of Cats that meet the specifications of the given Filter.

I'm having a hard time coming up with an efficient way of getting the desired results, ideally using LINQ/lambda expressions.

What is the best way to go about doing this sort of join? What extension methods should I be looking at? Modifying a collection in a foreach loop generally isn't advisable/possible, so what should my strategy be?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

咆哮 2024-11-14 02:07:25

我通常做的是在 where 子句中进行检查,在执行实际过滤器之前检查是否需要过滤器。当运行时需要评估过滤器时,如果不需要,则完全跳过它。

public class CatFilter
{
    public List<string> Names = new List<string>();
    public List<string> Colors = new List<string>();
    public DateTime? BirthDateStartRange = null;
    public DateTime? BirthDateEndRange = null;
}

public List<Cat> GetFilteredCats(CatFilter filter)
{
    List<Cat> result = new List<Cat>();

    var query = cats
        .Where(a => !filter.Names.Any() || filter.Names.Contains(a.Name))
        .Where(a => !filter.Colors.Any() || filter.Colors.Contains(a.Color))
        .Where(a => filter.BirthDateStartRange == null || a.DateOfBirth >= filter.BirthDateStartRange)
        .Where(a => filter.BirthDateEndRange == null || a.DateOfBirth <= filter.BirthDateEndRange);

    result.AddRange(query);
    return result;
}

然后这样称呼它

cats.Add(new Cat("Felix", "Black", DateTime.Today.AddDays(-1)));
cats.Add(new Cat("Garfield", "Orange", DateTime.Today.AddDays(-10)));

CatFilter filter = new CatFilter();
filter.Names.Add("Garfield");

List<Cat> result = GetFilteredCats(filter);

What i normally do is to put a check in the where clause which checks if the filter is needed before doing the actual filter. When the runtime needs to evaluate the filter, it is skipped entirely if it isn't needed.

public class CatFilter
{
    public List<string> Names = new List<string>();
    public List<string> Colors = new List<string>();
    public DateTime? BirthDateStartRange = null;
    public DateTime? BirthDateEndRange = null;
}

public List<Cat> GetFilteredCats(CatFilter filter)
{
    List<Cat> result = new List<Cat>();

    var query = cats
        .Where(a => !filter.Names.Any() || filter.Names.Contains(a.Name))
        .Where(a => !filter.Colors.Any() || filter.Colors.Contains(a.Color))
        .Where(a => filter.BirthDateStartRange == null || a.DateOfBirth >= filter.BirthDateStartRange)
        .Where(a => filter.BirthDateEndRange == null || a.DateOfBirth <= filter.BirthDateEndRange);

    result.AddRange(query);
    return result;
}

And then calling it like this

cats.Add(new Cat("Felix", "Black", DateTime.Today.AddDays(-1)));
cats.Add(new Cat("Garfield", "Orange", DateTime.Today.AddDays(-10)));

CatFilter filter = new CatFilter();
filter.Names.Add("Garfield");

List<Cat> result = GetFilteredCats(filter);
早乙女 2024-11-14 02:07:25

正确的方法是使用 GetFilteredCats 方法,接受您的过滤器并通过 LINQ 组合返回正确的猫:

IEnumerable<Cat> cats = //.. get all cats here

if (filter.FilterByColor)
   cats = cats.Where(c=>c.Color = filter.Color);

if (filter.FilterByName)
   cats = cats.Where(c=>c.Name = filter.Name);

if (filter.FilterByDate)
   cats = cats.Where(c=>c.Date > filter.FromDate && c.Date < filter.ToDate)

return cats.ToList(); // finally filter data and return them.

在性能方面。我认为这在逻辑上不能使用不同的方法来完成。但当你开始攻击数百万只猫时,这就会成为问题。此时就应该使用数据库来代替。为了您的方便,它们具有巧妙的索引和集群。

Correct way to do this is to make method GetFilteredCats, accept your filter and return correct cats throught LINQ composition:

IEnumerable<Cat> cats = //.. get all cats here

if (filter.FilterByColor)
   cats = cats.Where(c=>c.Color = filter.Color);

if (filter.FilterByName)
   cats = cats.Where(c=>c.Name = filter.Name);

if (filter.FilterByDate)
   cats = cats.Where(c=>c.Date > filter.FromDate && c.Date < filter.ToDate)

return cats.ToList(); // finally filter data and return them.

In case of performance. I don't think this can logicaly be done using different aproach. But this will become problem when you start hitting milions of cats. And at this point, database should be used instead. Those have clever indexing and clustering for your convenience.

优雅的叶子 2024-11-14 02:07:25

像这样的东西应该可以工作,请注意它没有经过测试

List<string> names = new List<string>();
            List<Color> colors = new List<Color>();
            List<DateTime> dobs = new List<DateTime>();

            List<cat> cats = new List<cat>();


            var filtered = from c in cats
                           join n in names on c.name equals n
                           join cl in colors on c.color equals cl
                           join db in dobs on c.dob equals db

                           select c;

您还可以有一些包含两个日期的列表,在这种情况下您需要放置一个 where 条件,其中 c.dob <= date1 && c.dob >= date2,或类似的东西。
希望这有帮助。

Something like this should work, please note it's not tested

List<string> names = new List<string>();
            List<Color> colors = new List<Color>();
            List<DateTime> dobs = new List<DateTime>();

            List<cat> cats = new List<cat>();


            var filtered = from c in cats
                           join n in names on c.name equals n
                           join cl in colors on c.color equals cl
                           join db in dobs on c.dob equals db

                           select c;

You can also have some list with two dates, in which case you'll need to put a where condition, where c.dob <= date1 && c.dob >= date2, or something similar.
Hope this helps.

偏爱自由 2024-11-14 02:07:25

您可以使用表达式树。当 CatFilter 对象传递到 GetFilteredCats 方法时,根据该对象上设置的属性,您将生成表达式(即在下面的伪代码中看到),您可以将其连接并用于构建完整的 LINQ 查询。

就像这样:

Expression catFilter =
from cat in Cats
    where <Expression> and <Expression> and ...
select cat

然后简单地编译(Expression.Compile)并执行。

You could use Expression trees. When a CatFilter object is passed to your GetFilteredCats method, based on the properties that are set on this object, you generate Expressions (i.e. seen in pseudo-code below) which you concatenate and use to build out a full LINQ query.

Something like:

Expression catFilter =
from cat in Cats
    where <Expression> and <Expression> and ...
select cat

Then simply compile (Expression.Compile) and execute.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文