0

This is my first question, so please sorry if something looks wrong.

I'm working in an app where i have the next models:

public class OrderDetails {
 int DetailID {get; set;}
 List<Taxes> taxes {get; set;}
}

public class Taxes {
 int TaxID {get; set;}
 decimal TaxValue {get; set;}
}

So in the code i have a List<OrderDetails> which one have a lot of items and that items have different taxes, but mostly items have the same taxes. I need to create a List<Taxes> general where i will have all the taxes that are in List<OrderDetails> and a sum of their values I think that is a group by using sum but i dont know how to apply the group by in a list inside a list.

Can you know if that is posible or not or if exist a different way to do that?

Thank u so much for your help!!

2 Answers2

1

Your criteria is not exactly clear so here are couple solutions which I believe is what you're looking for.

Use SelectMany to flatten the nested sequences and then group by the TaxId and then transform with Select:

var result = orderDetailses.SelectMany(x => x.taxes)
            .GroupBy(x => x.TaxID)
            .Select(x => new Taxes
            {
                TaxID = x.Key,
                TaxValue = x.Sum(e => e.TaxValue)                    
            }).ToList();

or you may be looking for:

var result = orderDetailses.SelectMany(o => o.taxes.Select(t => (TaxId: t.TaxID, orderDetailses: o)))
                .GroupBy(i => i.TaxId)
                .Select(e => new Taxes
                {
                    TaxID = e.Key,
                    TaxValue = e.SelectMany(x => x.orderDetailses.taxes)
                        .Sum(a => a.TaxValue)
                }).ToList();
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
0

You wrote:

items have different taxes, but mostly items have the same taxes

The most important thing for you to do is to define when two taxes are equal. Is it when they have equal TaxId and TaxValue, or is it when they have the same TaxValue, or should it be the same object. So which of these five Taxes are equal:

Taxes a = new Taxes {Taxid = 1, TaxValue = 3};
Taxes b = new Taxes {Taxid = 1, TaxValue = 4};
Taxes c = new Taxes {Taxid = 2, TaxValue = 4};
Taxes d = new Taxes {Taxid = 2, TaxValue = 4};
Taxes e = d;

If your answer would be: it depends: sometimes only d == e (same object), sometimes b == c == d == e (equal TaxValue), then you should provide an equality comparer: an object that implements IEqualityComparer<Taxes>.

Usually the answer is not so difficult, I think you want to say that two Taxes are equal if they have equal TaxValue

So your requirements are as follows:

  • Input: a sequence of OrderDetails. Every OrderDetail has a DetailId and a sequence of zero or more Taxes. Every Tax has a TaxId and a TaxValue.
  • Two Taxes are considered equal if they have the same TaxValue.
  • Requested output: From every OrderDetail the DetailId and a sequence of all its unique Taxes.

To select properties of input items use Enumerable.Select To keep only the Unique output values of a sequence use Enumerable.Distinct

var result = myOrderDetails               // take my sequence of OrderDetails
    .Select(orderDetail => new            // from every orderDetail in this sequence make one new object
    {                                     // with the following properties
         DetailId = orderDetail.DetailId,

         TaxValues = orderDetail.Taxes    // from all Taxes of this orderDetail,
            .Select(tax => tax.TaxValue)  // select only the TaxValues
            .Distinct(),                  // and remove duplicates

         // or if two taxValues are equal if they are the same object:
         Taxes = orderDatail.Taxes          // from all Taxes of this orderDetail
             .Distinct()                    // remove duplicates
             .Select(tax => tax.TaxValue),  // and keep only the TaxValue (optional)
    });

If your idea about equality of Taxes is not is not that simple, for instance if you would define equality as:

Two taxes are equal if they have equal TaxId, and equal TaxValue

In that case you'll have to write an equality comparer. They are not very difficult, however you should keep something in mind:

  • What to do with a Null-value? If both are null, are they equal?
  • Keep in mind that a derived class of a TaxValue is usually not equal to the TaxValue itself

Consider reading Define Value Equality for a Type

class TaxesEqualityComparer : EqualityComparer<TaxValue>
{

    public static readonly IEqualityComparer<TaxValue> ValueComparer
           = new TaxesEqualityComparer()

    public override bool Equals(TaxValue x, TaxValue y)
    {
        // the following lines are almost always the same:
        if (x == null) return y == null;               // true if both null
        if (y != null) return false;                   // because x not null and y is null
        if (Object.ReferenceEquals(x, y) return true;  // optimalization: same object
                                                       // no need to check the properties
         if (x.GetType() != y.GetType()) return false; // not same type

         // here start the differences of default Taxes comparison
         // when would you say that two Taxes are equal?
         return x.TaxId == y.TaxId
             && x.TaxValue == y.TaxValue;
    };

    public override int GetHashCode(x)
    {
        ...
    }
}

The GetHashCode is an optimization used by classes like Dictionaries, Sets, and functions like Distinct() to quickly find inequalities. There is no "best" way to define this function. Stack overflow: Best algorithm for GetHashCode might help you.

Once you've defined the class, you can use the equalicy comparer in Distinct:

// use default equality (= equal if same object)
...
.Distinct(TaxesEqualityComparer.Default)

// use your own definition of Taxes equality:
...
.Distinct(TaxesEqualityComparer.ValueComparer)
Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116