Thomas Lu
Thomas Lu

Reputation: 13

Is it possible to use LINQ to sum the value of multiple objects in a new object

Assuming I have an object class MyObject with the following properties:

class MyObject {
   public int MyProperty1 { get; set; }
   public int MyProperty2 { get; set; }
   public int MyProperty3 { get; set; }
}

And I have an array of MyObject[] with the following elements:

MyObject[] myObjects => new MyObject[] { myObject1, myObject2, myObject3 };

How do I create a new instance myObject such that its MyProperty1, MyProperty2, and MyProperty3 are the sums of the respective properties for every such object in the array?

Currently, my implementation is as follows

MyObject MyObjectSummed => new MyObject()
{
   MyProperty1 = myObjects.Sum(x => x.MyProperty1);
   MyProperty2 = myObjects.Sum(x => x.MyProperty2);
   MyProperty3 = myObjects.Sum(x => x.MyProperty3);
}

but I vaguely remember seeing a more efficient way of doing this using LINQ, using a single line of code.

Is this possible and can someone please point me in the right direction?

Thanks!

Upvotes: 1

Views: 996

Answers (3)

Andy Song
Andy Song

Reputation: 4684

So if you do not want to mutate the original array, this is what you can do:

    var result = myObjects.Aggregate(new MyObject(), (accumulate, current) => {
           accumulate.MyProperty1 += current.MyProperty1;
           accumulate.MyProperty2 += current.MyProperty2;
           accumulate.MyProperty3 += current.MyProperty3;
           return accumulate;
    });

If you do not care you can just do this:

By doing this way you are mutating the first element within the array.

    var result = myObjects.Aggregate((accumulate, current) => {
           accumulate.MyProperty1 += current.MyProperty1;
           accumulate.MyProperty2 += current.MyProperty2;
           accumulate.MyProperty3 += current.MyProperty3;
           return accumulate;
    });

Upvotes: 1

SomeBody
SomeBody

Reputation: 8743

If performance is not an issue, you can use reflections. Then you can add and remove integer properties to your object without having to modify the code of adding. If you convert the return value of GetProperties() to a list, you can use the ForEach() method of List<T>, which even reduces your line count further.

MyObject myObjectSummed = new MyObject();
foreach(var prop in myObjectSummed.GetType().GetProperties().Where(p => p.PropertyType == typeof(int)))
{
    prop.SetValue(myObjectSummed, myObjects.Sum(x => (int)prop.GetValue(x)));
}

But i recommend Fabio's answer: From my point of view, it's clearest way to write this logic. Having as few lines of code as possible is not always the best approach.

Upvotes: 0

Fabio
Fabio

Reputation: 32445

You need to update three properties, so having "one-liner" will make code very unreadable.

If asking about different LINQ approach instead of summarising three values, then Aggregate is your choice, check @Andy's answer.

If you wrap logic with the method then you can use any amount of lines inside the implementation, but keep it one-liner for the consumers.

Alternative approach can be an extension method for enumerable

public static MyObject CalculateSum(this IEnumerable<MyObject> objects)
{
    var total = new MyObject();
    foreach (var obj in objects)
    {
        total.MyProperty1 += obj.MyProperty1;
        total.MyProperty2 += obj.MyProperty2;
        total.MyProperty3 += obj.MyProperty3;
    }

    return total;
}

Usage is "one-liner" :)

var objects = new MyObject[] { myObject1, myObject2, myObject3 };

var sum = objects.CalculateSum();

Notice that all LINQ methods are extension methods, so you kinda using your own domain specific LINQ "one-liner" ;)

Upvotes: 2

Related Questions