如何使用Linq通过它的相等性在C#中对象进行分组

Rog*_*ira 1 c# 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; }       
}
Run Code Online (Sandbox Code Playgroud)

输入样本:

+-------------+-----+----------+
| OrderNumber | Sku | Quantity |
+-------------+-----+----------+
|           1 | A   |       10 |
|           1 | B   |       20 |
|           2 | A   |       10 |
|           3 | A   |       10 |
|           3 | B   |       20 |
+-------------+-----+----------+
Run Code Online (Sandbox Code Playgroud)

所需输出:

Lines = Lines.Count(); 每个分组相同的行数

Pieces = SUM(OrderLine.Quantity); 每个分组相同订单的所有数量的总和.

+-----------------+-------+--------+
| TotalIdenticals | Lines | Pieces |
+-----------------+-------+--------+
|               1 |     1 |     10 |
|               2 |     2 |     30 |
+-----------------+-------+--------+
Run Code Online (Sandbox Code Playgroud)

我使用表格表示使其更清晰.因此,如上所述,只有1条记录,包含1行(订单2)和数量10.另一方面,有两个订单具有相同的行列表(订单1和3)

所以我需要在运行linq算法之后,它会为我生成一个对象类型

> "identical 1".Orders -> [2]
> "identical 2".Order -> [1,3]
Run Code Online (Sandbox Code Playgroud)

我试着做什么?

var identicals = orderList.GroupBy(x => x.Lines)
                 .Where(g => g.Count() > 1)
                 .Select(g => g.Key)
                 .ToList();
Run Code Online (Sandbox Code Playgroud)

上面的代码没有工作,basicaly我只需要能够组Lines属性(因此它的平等与其他OrderLines),那么我就能生成我的线条/件......唯一的问题是现在输出能够按行列表对象相似性对我的对象顺序列表进行分组.

我希望我的问题清楚,如果你需要更多细节,请告诉我,我会在这里补充.

Rui*_*mba 5

第一步 - 为了GroupBy()OrderOrderItem类一起使用,您必须为两个类实现Equals()GetHashCode() OR创建EqualityComparer.

重写Equals(),并GetHashCode()Order(基于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}";
    }
}
Run Code Online (Sandbox Code Playgroud)

重写Equals(),并GetHashCode()OrderItem(基于两个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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

测试代码 - 订单列表:

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});
Run Code Online (Sandbox Code Playgroud)

分组订单:

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();
Run Code Online (Sandbox Code Playgroud)

输出:

产量