문제

I have a product object that has a certain location and allowable shipping methods for that product. What I'm trying to do is Group the products by location AND by the allowable ship methods.

For example the following example data would product two groupings one with IDLocation = 1 and ShipMethods of 1,2 with a count of 2 and the other would be IDLocation = 1 and ShipMethods of 1,2 with a count of 3.

public class CartProduct
    {
        public int IDLocation { get; set; }
        public List<int> ShipMethods { get; set; }

        public List<CartProduct> GetExampleData()
        {
            return new List<CartProduct>() { new CartProduct() { IDLocation = 1, ShipMethods = new List<int>(){ 1, 2 } },
                new CartProduct() { IDLocation = 1, ShipMethods = new List<int>(){ 1, 2 } },
                new CartProduct() { IDLocation = 1, ShipMethods = new List<int>(){ 3, 4 } },
                new CartProduct() { IDLocation = 1, ShipMethods = new List<int>(){ 3, 4 } },
                new CartProduct() { IDLocation = 1, ShipMethods = new List<int>(){ 3, 4 } }
            };
        }
    }

I would like to see a grouping of IDLocation first, then if the ship methods are the same group those together as well.

I've tried several version of group by and select many with no luck.

List<CartProduct> CPList = new CartProduct().GetExampleData();
var GroupItems  = CPList.GroupBy(x => x.IDLocation) // then by ShipMethods??
도움이 되었습니까?

해결책 2

The comparer argument in GroupBy allows you to define equality for purposes of the object grouping. The comparer is a separate class that compares two like objects and returns true if they are equal. The class, which needs to implement IComparer<CartItem>, can be implemented like this:

class CartGroupComparer : IEqualityComparer<CartProduct>
{
    public bool Equals(CartProduct x, CartProduct y)
    {
        return x.IDLocation == y.IDLocation
             && x.ShipMethods.OrderBy(x=>x)
                   .SequenceEqual(y.ShipMethods.OrderBy(x=>x));
    }

    public int GetHashCode(CartProduct obj)
    {
        return obj.IDLocation.GetHashCode() 
                ^ obj.ShipMethods.Sum().GetHashCode();
    }
}

(Note: for simplicity, this assumes that ShipMethods will never be null.)

The Equals method tests two items for equality; if equal, they will be added added to the same group. The GetHashCode method must return an equal value for equal items, and a simple implementation is above.

You can use this comparer directly in your GroupBy clause:

new CartProduct().GetExampleData()
         .GroupBy(a => a, new CartGroupComparer());

다른 팁

I think that the key is to use GroupBy's ability to specify a "key" to use when grouping. You want to group things together that have the same IDLocation and the same set of ShipMethods, so the key should include those things. Ideally you'd use a proper comparer that does the right thing. The hackish way to do this (which is easier to write, so I can tell this'll work) is to mash everything together into a string, so that the normal string comparison does what we want. So here's the quick answer:

var answer = GetExampleData()
             .GroupBy(x=>String.Format("{0} {1}",
                         x.IDLocation,
                         String.Join(",",x.ShipMethods.OrderBy(y=>y))));

For better performance, you'll have to implement the "proper" way I'm describing. It's a bit of work but it shouldn't be too hard.

edit: I am sorting the ShipMethods so that something that can be shipped via 1 or 2 is correctly seen to be the same thing as if it can be shipped via 2 or 1. Ideally the ShipMethods list is already sorted so we can save the time.

(the funky formatting is to try to make it visible without scrolling)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top