使用linq按数据表值分组

时间:2013-05-16 13:37:47

标签: linq datatable group-by

我想使用Linq和DataTable来获取以下内容

DataTable内容

periodstart periodend value
2013-01-01 2013-02-01 10
2013-02-02 2013-03-01 10
2013-03-02 2013-04-01 15
2013-04-02 2013-05-01 20
2013-05-02 2013-06-01 10
2013-06-02 2013-07-02 20

结果

2013-01-01 2013-03-01 10
2013-03-02 2013-04-01 15
2013-04-02 2013-05-01 20
2013-05-02 2013-06-01 10
2013-06-02 2013-07-02 20

基本上我想按值对句点进行分组,但如果在一段时间内有不同的分组,也允许重复相同的值。

我想使用分段的最小值和最大值来分组,但这会给我一些类似的东西

2013-01-01 2013-06-01 10
2013-03-02 2013-04-01 15
2013-04-02 2013-07-02 20

这是不正确的。

如何解决此问题?

1 个答案:

答案 0 :(得分:0)

(添加单独的答案,因为我现在删除的答案是错误的。)

听起来你只需要遍历所有行,保持一组直到值部分改变,然后取第一个元素的开头和最后一个元素的结尾。所以你可以做一些这样的扩展方法:

public static IEnumerable<IEnumerable<T>> GroupByContiguous<T, TKey>
    (this IEnumerable<T> source, Func<T, TKey> groupSelector)
{
    List<T> currentGroup = new List<T>();
    T groupKey = default(T);
    // This could be passed in as a parameter
    EqualityComparer<T> comparer = EqualityComparer<T>.Default;
    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            yield break;
        }
        groupKey = groupSelector(iterator.Current);
        currentGroup.Add(iterator.Current);
        while (iterator.MoveNext())
        {
            var item = iterator.Current;
            var key = groupSelector(item);
            if (!comparer.Equals(groupKey, key))
            {
                yield return currentGroup.Select(x => x);
                currentGroup = new List<T>();
                groupKey = key;
            }
            currentGroup.Add(item);
        }            
    }
    // Trailing group
    yield return currentGroup.Select(x => x);
}

然后将其用作:

var query = table.AsEnumerable()
                 .GroupByContiguous(row => row.Field<int>("value"))
                 .Select(g => new { 
                     Value = g.Key,
                     EarliestStart = g.First().Field<DateTime>("periodstart"),
                     LatestEnd = g.Last().Field<DateTime>("periodend")
                 });