簡體   English   中英

對對象列表進行分組[](希望與Linq一起使用)

[英]Group A List Of object[] (Hopefully With Linq)

說我有一個相等尺寸的對象數組的集合,像這樣:

var rows = new List<object[]>
{
    new object[] {1, "test1", "foo", 1},
    new object[] {1, "test1", "foo", 2},
    new object[] {2, "test1", "foo", 3},
    new object[] {2, "test2", "foo", 4},
};

我想對一個或多個“列”進行分組-這些列將在運行時動態確定。 例如,按1、2和3列分組將導致三組:

  • 組1:[1,“ test1”,“ foo”](包括第1行和第2行)
  • 組2:[2,“ test1”,“ foo”](包括第3行)
  • 組3:[2,“ test2”,“ foo”](包括第4行)

當然,我可以通過某種自定義組類以及排序和迭代來實現。 但是,看起來我應該可以使用Linq分組更干凈地進行此操作。 但是我的臨q夫使我失望。 有任何想法嗎?

如果您知道前面的分組列,則@Matthew Whited的解決方案很好。 但是,聽起來您需要在運行時確定它們。 在這種情況下,您可以創建一個相等比較器,該比較器使用可配置的列集為GroupBy定義行相等:

rows.GroupBy(row => row, new ColumnComparer(0, 1, 2))

比較器檢查每個指定列的值是否相等。 它還結合了每個值的哈希碼:

public class ColumnComparer : IEqualityComparer<object[]>
{
    private readonly IList<int> _comparedIndexes;

    public ColumnComparer(params int[] comparedIndexes)
    {
        _comparedIndexes = comparedIndexes.ToList();
    }

    #region IEqualityComparer

    public bool Equals(object[] x, object[] y)
    {
        return ReferenceEquals(x, y) || (x != null && y != null && ColumnsEqual(x, y));
    }

    public int GetHashCode(object[] obj)
    {
        return obj == null ? 0 : CombineColumnHashCodes(obj);
    }    
    #endregion

    private bool ColumnsEqual(object[] x, object[] y)
    {
        return _comparedIndexes.All(index => ColumnEqual(x, y, index));
    }

    private bool ColumnEqual(object[] x, object[] y, int index)
    {
        return Equals(x[index], y[index]);
    }

    private int CombineColumnHashCodes(object[] row)
    {
        return _comparedIndexes
            .Select(index => row[index])
            .Aggregate(0, (hashCode, value) => hashCode ^ (value == null ? 0 : value.GetHashCode()));
    }
}

如果您經常這樣做,則可以將其放在擴展方法后面:

public static IGrouping<object[], object[]> GroupByIndexes(
    this IEnumerable<object[]> source,
    params int[] indexes)
{
    return source.GroupBy(row => row, new ColumnComparer(indexes));
}

// Usage

row.GroupByIndexes(0, 1, 2)

擴展IEnumerable<object[]>僅適用於.NET4。您需要直接在.NET 3.5中擴展List<object[]>

如果您的集合包含帶有索引器的項目(例如您的object[] ,則可以這樣操作...

var byColumn = 3;

var rows = new List<object[]> 
{ 
    new object[] {1, "test1", "foo", 1}, 
    new object[] {1, "test1", "foo", 2}, 
    new object[] {2, "test1", "foo", 3}, 
    new object[] {2, "test2", "foo", 4}, 
};

var grouped = rows.GroupBy(k => k[byColumn]);
var otherGrouped = rows.GroupBy(k => new { k1 = k[1], k2 = k[2] });

...如果您不喜歡上面的靜態集,則也可以直接在LINQ中做一些更有趣的事情。 這將假定您的HashCodes將適用於Equals評估。 注意,您可能只想編寫IEqualityComparer<T>

var cols = new[] { 1, 2};

var grouped = rows.GroupBy(
    row => cols.Select(col => row[col])
               .Aggregate(
                    97654321, 
                    (a, v) => (v.GetHashCode() * 12356789) ^ a));

foreach (var keyed in grouped)
{
    Console.WriteLine(keyed.Key);
    foreach (var value in keyed)
        Console.WriteLine("{0}|{1}|{2}|{3}", value);
}

最短的解決方案:

    int[] columns = { 0, 1 };

    var seed = new[] { rows.AsEnumerable() }.AsEnumerable();    // IEnumerable<object[]> = group, IEnumerable<group> = result

    var result = columns.Aggregate(seed, 
        (groups, nCol) => groups.SelectMany(g => g.GroupBy(row => row[nCol])));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM