簡體   English   中英

如何使用Linq通過相等性將C#中的對象分組

[英]How to group object in C# by its equality using Linq

場景:

我有一個對象“ Order ”的列表,並希望按相同的List<OrderLine>屬性分組,相同的意思是相同數量的行以及相同訂單中相同的Sku / Quantity值”並返回訂單列表值分組:

class Order
{
    public int OrderNumber { get; set; }
    public List<OrderLine> Lines { get; set; }
}

class OrderLine
{    
    public string Sku { get; set; }
    public int Quantity { get; set; }       
}

輸入樣本:

+-------------+-----+----------+
| OrderNumber | Sku | Quantity |
+-------------+-----+----------+
|           1 | A   |       10 |
|           1 | B   |       20 |
|           2 | A   |       10 |
|           3 | A   |       10 |
|           3 | B   |       20 |
+-------------+-----+----------+

所需的輸出:

Lines = Lines.Count(); 每個分組相同的行數

Pieces = SUM(OrderLine.Quantity); 每個相同訂單的所有數量之和。

+-----------------+-------+--------+
| TotalIdenticals | Lines | Pieces |
+-----------------+-------+--------+
|               1 |     1 |     10 |
|               2 |     2 |     30 |
+-----------------+-------+--------+

我使用表表示法使其更加清晰。 因此,如上所述,只有1條記錄包含1行(第2階)和數量10。另一方面,有兩個記錄具有相同的行列表(第1和3階)

所以我需要在運行linq算法后,它將為我生成一種

> "identical 1".Orders -> [2]
> "identical 2".Order -> [1,3]

我想做什么?

var identicals = orderList.GroupBy(x => x.Lines)
                 .Where(g => g.Count() > 1)
                 .Select(g => g.Key)
                 .ToList();

上面的代碼不起作用,基本上我只需要能夠將Lines屬性分組(這樣就等於其他OrderLines),那么我將能夠生成行/片段的輸出...現在唯一的問題是能夠通過行列表對象相似性對我的對象順序列表進行分組。

我希望我的問題很清楚,如果您需要更多詳細信息,請告訴我,我將在這里添加。

第一步-為了在OrderOrderItem類上使用GroupBy() ,必須實現Equals()GetHashCode()或為這兩個類創建EqualityComparer

Order覆蓋Equals()GetHashCode() (僅基於Lines屬性):

public class Order
{
    public int OrderNumber { get; set; }
    public List<OrderLine> Lines { get; set; }

    protected bool Equals(Order other)
    {
        var equals = OrderLinesEquals(Lines, other.Lines);

        return equals;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((Order) obj);
    }

    public override int GetHashCode()
    {
        if (Lines == null)
        {
            return 0;
        }

        unchecked
        {
            int hash = 19;

            foreach (OrderLine item in Lines.OrderBy(x => x.Sku, StringComparer.OrdinalIgnoreCase))
            {
                hash = hash * 31 + item.GetHashCode();
            }

            return hash;
        }
    }

    private bool OrderLinesEquals(List<OrderLine> x, List<OrderLine> y)
    {
        if (ReferenceEquals(x, y))
        {
            return true;
        }

        if (x == null || y == null)
        {
            return false;
        }

        bool areEquivalent = x.Count == y.Count && !x.Except(y).Any();

        return areEquivalent;
    }

    public override string ToString()
    {
        return $"Sku: {Sku ?? "[null]"}, Quantity: {Quantity}";
    }
}

OrderItem重寫Equals()GetHashCode() (基於SkuQuantity屬性):

public class OrderLine
{
    public string Sku { get; set; }
    public int Quantity { get; set; }

    protected bool Equals(OrderLine other)
    {
        return string.Equals(Sku, other.Sku) && Quantity == other.Quantity;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((OrderLine) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((Sku != null ? Sku.GetHashCode() : 0) * 397) ^ Quantity;
        }
    }
}

測試代碼-訂單清單:

var order1 = new Order
{
    OrderNumber = 1,
    Lines = new List<OrderLine>
    {
        new OrderLine
        {
            Quantity = 10,
            Sku = "A"
        },

        new OrderLine
        {
            Quantity = 20,
            Sku = "B"
        }
    }
};

var order2 = new Order
{
    OrderNumber = 2,
    Lines = new List<OrderLine>
    {
        new OrderLine
        {
            Quantity = 10,
            Sku = "A"
        }
    }
};

var order3 = new Order
{
    OrderNumber = 3,
    Lines = new List<OrderLine>
    {
        new OrderLine
        {
            Quantity = 20,
            Sku = "B"
        },
        new OrderLine
        {
            Quantity = 10,
            Sku = "A"
        }
    }
};


var order4 = new Order
{
    OrderNumber = 4,
    Lines = new List<OrderLine>
    {
        new OrderLine
        {
            Quantity = 20,
            Sku = "B"
        },
        new OrderLine
        {
            Quantity = 10,
            Sku = "A"
        }
    }
};


var order5 = new Order
{
    OrderNumber = 5,
    Lines = new List<OrderLine>
    {
        new OrderLine
        {
            Quantity = 30,
            Sku = "C"
        }
    }
};


var order6 = new Order
{
    OrderNumber = 6,
    Lines = new List<OrderLine>
    {
        new OrderLine
        {
            Quantity = 40,
            Sku = "C"
        }
    }
};


var order7 = new Order
{
    OrderNumber = 7,
    Lines = new List<OrderLine>
    {
        new OrderLine
        {
            Quantity = 30,
            Sku = "C"
        }
    }
};

var orderList = new List<Order>(new[] {order1, order2, order3, order4, order5, order6, order7});

分組訂單:

var identicalOrders = orderList.GroupBy(x => x)
                               .Where(g => g.Count() > 1)
                               .Select(g => new
                               {
                                   Count = g.Count(),
                                   OrderItems = g.Key.Lines,
                                   OrderNumbers = orderList.Where(x => x.Equals(g.Key))
                                                           .Select(x => x.OrderNumber)
                                                           .ToList()
                               })
                               .ToList();

輸出:

輸出量

為了能夠按Lines分組,您需要實現IEqualityComparer<List<OrderLine>>並將其傳遞給GroupBy方法: var groups = orders.GroupBy(o => o.Lines, o => o, new OrderLineEqualityComparer());

internal class OrderLineEqualityComparer : IEqualityComparer<List<OrderLine>>
{
    public bool Equals(List<OrderLine> x, List<OrderLine> y)
    {
        throw new NotImplementedException();
    }

    public int GetHashCode(List<OrderLine> obj)
    {
        throw new NotImplementedException();
    }
}

暫無
暫無

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

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