LINQ groupBy包含来自字符串数组的多个列

时间:2014-07-07 13:07:32

标签: c# linq grouping

我有以下代码:

string[] tokens; //columns which I want to groupBy (e.g., storeID, location)
Dictionary<string, List<DataRow>> dictShort = null; // what I want to return
switch (tokens.Length)
{
    case 1:
        dictShort = dsShort.Tables[0].AsEnumerable()
            .GroupBy(x => x[block])
            .Where(g => exp.GroupSizeOk((uint)g.Count()))
            .OrderBy(g => g.Count())
            .ToDictionary(g => g.Key.ToString(), g => g.ToList());
        break;
    case 2:
        dictShort = (Dictionary<string, List<DataRow>>)
            dsShort.Tables[0].AsEnumerable()
            .GroupBy(x => x[tokens[0]], x => x[tokens[1]])
            .Where(g => exp.GroupSizeOk((uint)g.Count()))
            .OrderBy(g => g.Count())
            .ToDictionary(g => g.Key.ToString(), g => g.ToList());
            // NOT COMPILING> cannot convert Dictionary<string, List<objet>>
            // to Dictionary<string, List<DataRow>>
        break;
    case 3:
        dictShort = (Dictionary<string, List<DataRow>>)
            dsShort.Tables[0].AsEnumerable()
            .GroupBy(x => new { x[tokens[0]], x[tokens[1]], x[tokens[2]]})
            .Where(g => exp.GroupSizeOk((uint)g.Count()))
            .OrderBy(g => g.Count())
            .ToDictionary(g => g.Key.ToString(), g => g.ToList());
           // NOT COMPILING: invalid anonymous type member declarator
        break;
}

我的问题:

(1)案例3不起作用,我该如何纠正?

(2)我可以让这个动态吗? (即,foreach或类似的东西,以便它适用于任意数量的colunms)

public bool GroupSizeOk(UInt32 size)
{
    return (size >= _minGroupSize) 
        && (_maxGroupSize > 0 ? size <= _maxGroupSize : true);
}

3 个答案:

答案 0 :(得分:1)

您需要的是数组的IEqualityComparer,它将根据数组中的项比较数组,而不是数组本身的引用。这样的比较器很简单,可以创建:

public class SequenceComparer<T> : IEqualityComparer<IEnumerable<T>>
{
    private IEqualityComparer<T> itemComparer;
    public SequenceComparer(IEqualityComparer<T> itemComparer = null)
    {
        this.itemComparer = itemComparer ?? EqualityComparer<T>.Default;
    }
    public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
    {
        return x.SequenceEqual(y);
    }

    public int GetHashCode(IEnumerable<T> obj)
    {
        unchecked
        {
            return obj.Aggregate(79, 
                (hash, next) => hash * 39 + next.GetHashCode());
        }
    }
}

现在,您的查询基本上可以按照您的预期工作,另一个关键更改是将数组tokens转换为表示给定数组中该行的值的数组:

var dictShort = dsShort.Tables[0].AsEnumerable()
            .GroupBy(row => tokens.Select(token => row[token]).ToArray(),
                new SequenceComparer<object>())
            .Where(g => exp.GroupSizeOk((uint)g.Count()))
            .OrderBy(g => g.Count())
            .ToDictionary(g => string.Join(", ", g.Key), g => g.ToList());

答案 1 :(得分:-1)

你有:

        .GroupBy(new x => { x[tokens[0]], x[tokens[1]], x[tokens[2]]})

但它应该是:

        .GroupBy(x => string.Join(",",new [] { x[tokens[0]], x[tokens[1]], x[tokens[2]]}))

在我意识到这是你答案的一部分之前,我已经改变了你的问题。新的内容属于lambda。

至于根据列使代码动态化,是的,你可以。

        .GroupBy(x => string.Join(",",tokens.Where(w => w.Columns.Contains(w)).Select(s => x[s]).ToArray()))

这应该为您提供数组中所有匹配列的分组。

答案 2 :(得分:-1)

我刚刚找到答案:

 dictShort = dsShort.Tables[0].AsEnumerable()
// This where selects elements if and only if all fields are not null
.Where(x => ListAnd(tokens.Select(t => x[t] != DBNull.Value && IsFilledIn(x[t].ToString())).ToArray()))
.GroupBy(x => String.Join("+", tokens.Select(t => x[t].ToString()).ToArray()))
//.GroupBy(x => x[block]) // TODO
.Where(g => exp.GroupSizeOk((uint)g.Count()))
.OrderBy(g => g.Count())
.ToDictionary(g => g.Key/*.ToString()*/, g => g.ToList());