ILookup与 IGrouping 的比较

发布于 2024-08-02 20:11:00 字数 1271 浏览 13 评论 0 原文

我一直无法阐明 ILookup< 之间的差异/a> 和 IGrouping,并且很好奇我现在理解是否正确。 LINQ 通过生成 IGrouping 项序列,同时还提供了 ToLookup 扩展方法,使问题更加复杂。所以感觉它们是一样的,直到我仔细观察。

var q1 = 
    from n in N
    group n by n.MyKey into g
    select g;
// q1 is IEnumerable<IGrouping<TKey, TVal>>

这相当于:

var q2 = N.GroupBy(n => n.MyKey, n => n);
// q2 is IEnumerable<IGrouping<TKey, TVal>>

这看起来很像:

var q3 = N.ToLookup(n => n.MyKey, n => n);
// q3 is ILookup<TKey, TVal>

我在以下类比中正确吗?

  1. IGrouping 是单个组(即键控序列),类似于 KeyValuePair,其中值实际上是元素序列 (而不是单个元素)
  2. IEnumerable> 是这些序列的序列(类似于迭代 IDictionaryIDictionary 时得到的结果) code>
  3. ILookup 更像是 IDictionary,其中值实际上是元素序列

I've been having trouble articulating the differences between ILookup<TKey, TVal> and IGrouping<TKey, TVal>, and am curious if I understand it correctly now. LINQ compounded the issue by producing sequences of IGrouping items while also giving me a ToLookup extension method. So it felt like they were the same until I looked more closely.

var q1 = 
    from n in N
    group n by n.MyKey into g
    select g;
// q1 is IEnumerable<IGrouping<TKey, TVal>>

Which is equivalent to:

var q2 = N.GroupBy(n => n.MyKey, n => n);
// q2 is IEnumerable<IGrouping<TKey, TVal>>

Which looks a lot like:

var q3 = N.ToLookup(n => n.MyKey, n => n);
// q3 is ILookup<TKey, TVal>

Am I correct in the following analogies?

  1. An IGrouping<TKey, TVal> is a single group (i.e. a keyed sequence), analogous to KeyValuePair<TKey, TVal> where the value is actually a sequence of elements (rather than a single element)
  2. An IEnumerable<IGrouping<TKey, TVal>> is a sequence of those (similar to what you get when iterating over an IDictionary<TKey, TVal>
  3. An ILookup<TKey, TVal> is more like a IDictionary<TKey, TVal> where the value is actually a sequence of elements

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

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

发布评论

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

评论(3

终止放荡 2024-08-09 20:11:00

是的,所有这些都是正确的。

并且 ILookup 还扩展了 IEnumerable>,因此您可以迭代所有键/​​集合对(或者改为of) 只是查找特定的键。

我基本上认为 ILookup 就像 IDictionary> 一样。

请记住,ToLookup 是“立即执行”操作(立即执行),而 GroupBy 是延迟操作。碰巧的是,按照“pull LINQ”的工作方式,当您开始从 GroupBy 的结果中提取 IGrouping 时,它必须读取所有数据(因为你不能中途切换组),而在其他实现中它可能能够产生流结果。 (在 Push LINQ 中确实如此;我希望 LINQ to Events 也是如此。)

Yes, all of those are correct.

And ILookup<TKey, TValue> also extends IEnumerable<IGrouping<TKey, TValue>> so you can iterate over all the key/collection pairs as well as (or instead of) just looking up particular keys.

I basically think of ILookup<TKey,TValue> as being like IDictionary<TKey, IEnumerable<TValue>>.

Bear in mind that ToLookup is a "do it now" operation (immediate execution) whereas a GroupBy is deferred. As it happens, with the way that "pull LINQ" works, when you start pulling IGroupings from the result of a GroupBy, it has to read all the data anyway (because you can't switch group midway through) whereas in other implementations it may be able to produce a streaming result. (It does in Push LINQ; I would expect LINQ to Events to be the same.)

胡渣熟男 2024-08-09 20:11:00

GroupByToLookUp 具有几乎相同的功能除了参考

GroupBy:GroupBy 运算符根据某些条件返回元素组
关键值。每个组由 IGrouping 表示
目的。

ToLookup:ToLookup与GroupBy相同;唯一的区别
GroupBy 的执行被推迟,而 ToLookup 的执行被推迟
立即。

让我们使用示例代码来消除差异。假设我们有一个代表 Person 模型的类:

class Personnel
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public int Level { get; set; }
}

之后我们定义一个 personnel 列表,如下所示:

 var personnels = new List<Personnel>
    {
        new Personnel { Id = 1, FullName = "P1", Level = 1 },
        new Personnel { Id = 2, FullName = "P2", Level = 2 },
        new Personnel { Id = 3, FullName = "P3", Level = 1 },
        new Personnel { Id = 4, FullName = "P4", Level = 1 },
        new Personnel { Id = 5, FullName = "P5", Level =2 },
        new Personnel { Id = 6, FullName = "P6", Level = 2 },
        new Personnel { Id = 7, FullName = "P7", Level = 2 }
    };

现在我需要将 personnels 分组按他们的水平。我这里有两种方法。使用GroupByToLookUp。如果我使用GroupBy,如前所述,它将使用延迟执行,这意味着,当您迭代集合时,下一个项目可能会也可能不会被计算,直到它被调用为止。

 var groups = personnels.GroupBy(p => p.Level);
    personnels.RemoveAll(p => p.Level == 1);
    foreach (var product in groups)
    {
        Console.WriteLine(product.Key);
        foreach (var item in product)
            Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
    }

在上面的代码中,我首先对人员进行了分组,但在迭代之前,我删除了一些人员。由于GroupBy采用的是延迟执行,所以最终的结果不会包含被移除的项,因为分组会在此处的foreach点进行计算。

输出:

2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2

但是如果我将上面的代码重写如下:(注意,代码与之前的代码相同,除了 GroupByToLookUp 替换)

 var groups = personnels.ToLookup(p => p.Level);
    personnels.RemoveAll(p => p.Level == 1);
    foreach (var product in groups)
    {
        Console.WriteLine(product.Key);
        foreach (var item in product)
            Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
    }

As ToLookUp 使用立即执行,这意味着当我调用 ToLookUp 方法时,会生成结果并应用组,因此如果我在迭代之前从 personnels 中删除任何项目,不会影响最终结果。

输出:

1
1 >>> P1 >>> 1
3 >>> P3 >>> 1
4 >>> P4 >>> 1
2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2

注意:GroupByToLookUp 也返回不同的类型。

您可以使用 ToDictionary 而不是 ToLookUp,但需要注意这一点:(参考)

ToLookup()的用法与ToDictionary()非常相似,
两者都允许您指定键选择器、值选择器和
比较者。主要区别在于 ToLookup() 允许(并且
需要)重复的键,而 ToDictionary() 则不需要

GroupBy and ToLookUp has almost same functionality EXCEPT this: Reference

GroupBy: The GroupBy operator returns groups of elements based on some
key value. Each group is represented by IGrouping
object.

ToLookup: ToLookup is the same as GroupBy; the only difference
is the execution of GroupBy is deferred whereas ToLookup execution is
immediate.

Lets clear the difference using sample code. suppose that we have a class representing Person model:

class Personnel
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public int Level { get; set; }
}

after that we define a list of personnels as below:

 var personnels = new List<Personnel>
    {
        new Personnel { Id = 1, FullName = "P1", Level = 1 },
        new Personnel { Id = 2, FullName = "P2", Level = 2 },
        new Personnel { Id = 3, FullName = "P3", Level = 1 },
        new Personnel { Id = 4, FullName = "P4", Level = 1 },
        new Personnel { Id = 5, FullName = "P5", Level =2 },
        new Personnel { Id = 6, FullName = "P6", Level = 2 },
        new Personnel { Id = 7, FullName = "P7", Level = 2 }
    };

Now I need to get the personnels grouped by their level. I have two approach here. using GroupBy or ToLookUp. If I use GroupBy, as stated before, it will use deferred execution, this means, that when you iterate through the collection the next item may or may not be computed until it is called for.

 var groups = personnels.GroupBy(p => p.Level);
    personnels.RemoveAll(p => p.Level == 1);
    foreach (var product in groups)
    {
        Console.WriteLine(product.Key);
        foreach (var item in product)
            Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
    }

In the above code, I firstly grouped the personnels, but before iterating it, I removed some personnels. As GroupBy uses deferred execution, so the final result will not include the removed items, because grouping will be computing in the foreach point here.

Output:

2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2

But if I rewrite the above code as below:(note that code is same as the previous code except GroupBy is replaced by ToLookUp)

 var groups = personnels.ToLookup(p => p.Level);
    personnels.RemoveAll(p => p.Level == 1);
    foreach (var product in groups)
    {
        Console.WriteLine(product.Key);
        foreach (var item in product)
            Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
    }

As ToLookUp uses immediate execution, it means that when I call the ToLookUp method, result is generated and group is applied, so if I remove any item from personnels prior to iteration, that wont effect the final result.

Output:

1
1 >>> P1 >>> 1
3 >>> P3 >>> 1
4 >>> P4 >>> 1
2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2

Note: GroupBy and ToLookUp both return different types too.

You might use ToDictionary instead of ToLookUp, but you need to pay attention to this:(reference)

The usage of ToLookup() is very similar to that of ToDictionary(),
both allow you to specify key selectors, value selectors, and
comparers. The main difference is that ToLookup() allows (and
expects) the duplicate keys whereas ToDictionary() does not

傲影 2024-08-09 20:11:00

ILookup 和 IDictionary 之间还有另一个重要区别:前者强制执行不变性,因为没有方法可以更改数据(除非使用者执行显式强制转换)。相比之下,IDictionary 具有“Add”等允许更改数据的方法。因此,从函数式编程和/或并行编程的角度来看,ILookup 更好。

(顺便说一句,似乎值得指出的是,IEnumerable 和 IList 之间的关系有点类似于 ILookup 和 IDictionary 之间的关系 - 前者是不可变的,后者不是。)

There is another important difference between ILookup and IDictionary: the former enforces immutability in the sense that here are no methods for changing the data (except when the consumer performs an explicit cast). By contrast, IDictionary has methods like "Add" which allow changing the data. So, from the perspective of functional-programming and/or parallel programming, ILookup is nicer.

(Btw., it seems worth pointing out that the relationship between IEnumerable and IList is somewhat similar to the one between ILookup and IDictionary - the former is immutable, the latter is not.)

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