【问题标题】:How to efficiently limit and then concatenate a result with a linq / lambda expression?如何有效地限制结果,然后将结果与 linq / lambda 表达式连接起来?
【发布时间】:2013-02-14 21:23:10
【问题描述】:

我正在创建一项服务,以使用户可以轻松地从IANA - Protocol Registry 中选择协议。

正如您可能想象的那样,在注册表中搜索术语 http 会获得很多点击。由于用户选择amt-soap-http 的频率比直接选择http 的频率要低得多,因此我决定提取以http 开头的所有内容,然后将其与其余结果连接起来。

下面的 lambda 表达式是该思考过程的结果:

var records = this._ianaRegistryService.GetAllLike(term).ToList();
var results = records.Where(r => r.Name.StartsWith(term))
                     .OrderBy(r => r.Name)
                     .Concat(records.Where(r => !r.Name.StartsWith(term))
                                    .OrderBy(r => r.Name))
                     .Take(MaxResultSize);

不幸的是,我觉得我对结果的迭代次数超出了必要的次数。除了过早的优化考虑之外,是否存在比上述更有效的 lambda 表达式组合?

【问题讨论】:

  • 对于那些不知道OrderbyGroupBy+SelectMany 之间区别的人(如@Bobson 和支持者):一个是n*Log(n) 操作,另一个是n
  • @I4V 使用这些知识有没有比answer D Stanley provided 更有效的答案?
  • ahsteele,我给了一个,但在 3 次否决后我删除了它(因为我写了 我还没有测试过
  • @I4V 很抱歉你被否决了。不幸的是,我没有看到答案。
  • MaxResultSize 多久超过以term 开头的名称数量?考虑到Concat 的懒惰性质,但 eager 的方式是在您获取第一个值后立即按OrderBy 对值进行排序,要求 1000 个项目和要求 1001 个项目之间的区别可能是巨大的。

标签: c# lambda concat


【解决方案1】:

两步排序可能更有效:

var results = records.OrderBy(r => r.Name.StartsWith(term) ? 1 : 2)
                     .ThenBy(r => r.Name)
                     .Take(MaxResultSize);

【讨论】:

  • @L.B 有没有比 D Stanley 和我的代码更有效的替代方案?
  • @DStanley 假设 n=2**20 (1M),您的解决方案将是 1M*20,但对两个 0.5M 项进行排序将是 (0.5M*19)*2。此外,您还有ThenBy,这也是(0.5M*19)*2
  • @L.B 我不能。我开玩笑说我还没有超能力,因为我的声誉较低。感谢您提供它。 :)
  • @L.B - 它被否决的部分原因(我认为然后被删除)是因为其中一部分是无用的。它产生与records.OrderBy(x => !x.Name.StartsWith(term)) 完全相同的结果。所以不需要分组或多选。
【解决方案2】:

使用评论来解释我正在尝试做的事情变得越来越难。所以我会发布另一个答案。 假设我想先根据随机整数列表的偶数或奇数排序,然后按数字顺序(模拟StartsWithmod 2)。

这里是测试用例:action2和其他答案一样。

如果你运行这段代码,你会发现我的建议 (action1) 快了两倍。

void Test()
{
    Random rnd = new Random();
    List<int> records = new List<int>();
    for(int i=0;i<2000000;i++)
    {
        records.Add(rnd.Next());
    }

    Action action1 = () =>
    {
        var res1 = records.GroupBy(r => r % 2)
                    .OrderBy(x => x.Key)
                    .Select(x => x.OrderBy(y => y))
                    .SelectMany(x => x)
                    .ToList();
    };

    Action action2 = () =>
    {
        var res2 = records.OrderBy(x => x % 2).ThenBy(x => x).ToList();
    };


    //Avoid counting JIT
    action1();
    action2();


    var sw = Stopwatch.StartNew();
    action1();
    long t1 = sw.ElapsedMilliseconds;

    sw.Restart();
    action2();
    long t2 = sw.ElapsedMilliseconds;

    Console.WriteLine(t1 + " " + t2);
}

【讨论】:

  • 当每个组只有一条记录时,对组进行排序的效率低于对单个记录进行排序。
  • @Bobson 试试吧。 我说它快 2 倍
  • 结果当每组只有一条记录时3302 615。我拿走了你的代码,用i 替换了rnd.Next(),并取消了% 2 的调用。这是 OP 问题的更接近的近似值,他的列表中的每个值都是唯一的,也是我一直试图提出的观点。如果你也从两者中取出二阶排序,结果是2656 568
  • @Bobson 当您拨打%2 电话时,您会更改所有逻辑。 %2 模拟 startsWith 方法。所以你有2组。随机部分与我的答案无关,但使用它更好地模仿真实的排序。
  • 这是一个有效的观点。我一直在取消您在评论中发布的代码,您在 .Name 而不是 .StartsWith(.Name) 上分组,因为原始代码不再可见。这个例子 比我想象的更接近 OP 的问题。
猜你喜欢
  • 1970-01-01
  • 2015-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-15
  • 2021-05-23
相关资源
最近更新 更多