【问题标题】:'Smart' grouping with LINQ使用 LINQ 进行“智能”分组
【发布时间】:2011-06-20 08:42:39
【问题描述】:

我有一个字符串列表,我想将其转换为某种分组列表,其中值将按它们在列表中的位置进行分组(不是正常分组,但在某种程度上,相同的项目在仅当他们在一起时才成为一组)。考虑以下示例:

LinkedList<string> myList = new LinkedList<string>();
myList.AddLast("aaa");
myList.AddLast("aaa");
myList.AddLast("bbb");
myList.AddLast("bbb");
myList.AddLast("aaa");
myList.AddLast("aaa");
myList.AddLast("aaa");

LinkedList<MyTuple> groupedList = new LinkedList<MyTuple>();
groupedList.AddLast(new MyTuple("aaa", 2));
groupedList.AddLast(new MyTuple("bbb", 2));
groupedList.AddLast(new MyTuple("aaa", 3));

这种转换可以用 LINQ 完成,还是我应该用循环编写通常的算法?

【问题讨论】:

  • 第二个 sn-p 不会运行,因为您要使用相同的键两次添加值。
  • 是的,我看到了,编辑了问题:)

标签: c# .net linq list dictionary


【解决方案1】:

this answer 的扩展方法几乎可以满足您的要求(微软也提供了一个implementation to group contiguous items in a sequence):

public static IEnumerable<IGrouping<int, T>> 
    GroupConsecutive<T>(this IEnumerable<T> set, Func<T, T, bool> predicate)
{
    var i = 0;
    var k = 0;
    var ranges = from e in set
                 let idx = ++i
                 let next = set.ElementAtOrDefault(idx)
                 let key = (predicate(e, next)) ? k : k++
                 group e by key into g
                 select g;
    return ranges;
}

你可以这样使用它:

void Main()
{
    LinkedList<string> myList = new LinkedList<string>();
    myList.AddLast("aaa");
    myList.AddLast("aaa");
    myList.AddLast("bbb");
    myList.AddLast("bbb");
    myList.AddLast("aaa");
    myList.AddLast("aaa");
    myList.AddLast("aaa");
    IGrouping<int,string> ggg;

    var groups=myList.GroupConsecutive((a,b)=>a==b);

    ILookup<string,int> lookup=groups.ToLookup(g=>g.First(),g=>g.Count());

    foreach(var x in lookup["aaa"])
    {
        Console.WriteLine(x); //outputs 2 then 3
    }
    foreach(var x in lookup["bbb"])
    {
        Console.WriteLine(x); //outputs 2
    }

}

请注意,最终容器是 ILookup,其行为有点像字典,但允许针对单个键存储多个值。

【讨论】:

  • 如前所述,GroupConsecutive 上次使用“默认”调用比较器。这似乎很糟糕(尤其是在我的情况下,因为我的比较器没有预料到它会因 NullReferenceException 而崩溃。)
  • @ScottStafford :我会在这里使用链接的 MS 解决方案。我使用它取得了很大的成功。
【解决方案2】:

这在字典中是不可能的。字典是关联的(即:每个键必须指向一个并且只指向一个)并且本质上是无序的。您需要为该数据结构使用其他东西。不过这不会太难!

编辑

List&lt;Tuple&lt;string, int&gt;&gt; 应该可以解决问题:

List<KeyValuePair<string, int>> structure = new List<KeyValuePair<string, int>>();
structure.Add(new KeyValuePair<string, int>(myList[0], 1);
for(int i = 0; i < myList.Count; i++ )
{
    if( myList[i] == structure[structure.Count-1].Key )
    {
        structure[structure.Count-1].Value += 1;
    }
    else
    {
        structure.Add(new KeyValuePair<string, int>(myList[i], 1);
    }
}

之后,您应该(未经测试!)拥有您正在寻找的东西。

编辑(再想一想)

虽然可以使用 linq(使用 TakeWhile 和计数...),但我仍然认为在这里使用循环更有意义,它很简单。比我更聪明的人可以尝试与 Linq 合作。

【讨论】:

  • 说,List&lt;KeyValuePair&lt;string, int&gt;&gt;?
  • 我认为 ILookup 接口已经存在“其他”容器(请参阅我的答案)
  • @spender:ILookup 是否保留了组的相互顺序? (例如,如果我有 aabbccaa,我能找出第二组 a 相对于其他 a 的位置吗?)
  • 我找不到任何文档可以用一种或另一种方式说。我的简单测试说是的,但不幸的是,不能依赖简单的观察。
  • stackoverflow.com/questions/1189982/… 表示确实如此,但即使这样也不能保证它总是如此。
猜你喜欢
  • 2011-01-13
  • 1970-01-01
  • 2021-06-30
  • 2013-01-23
  • 1970-01-01
  • 2010-10-17
  • 2016-08-08
  • 2013-04-11
  • 2016-04-02
相关资源
最近更新 更多