3
\$\begingroup\$

I am wondering which one I should choose between the following two options when I need to calculate the sum of Data of the student with Id of 1.

var out1 = students
            .Select(x => new { Id = x.Id, Sum = x.Data.SelectMany(n => n).Sum() })
            .FirstOrDefault(s => s.Id == 1)
            .Sum;

var out2 = students
            .FirstOrDefault(s => s.Id == 1)
            .Data.SelectMany(n => n)
            .Sum();

Any comments are welcome.

class Student
{
    public int Id { get; set; }
    public List<List<int>> Data { get; set; }
}

class Program
{
    static void Main()
    {
        var a = new Student
        {
            Id = 1,
            Data = new List<List<int>>
            {
                new List<int>{ 1,2,3},
                new List<int>{ 4,5,6}
            }
        };

        var b = new Student
        {
            Id = 2,
            Data = new List<List<int>>
            {
                new List<int>{ 7,8,9},
                new List<int>{ 10,11,12}
            }
        };

        var c = new Student
        {
            Id = 3,
            Data = new List<List<int>>
            {
                new List<int>{ 13,14,15},
                new List<int>{ 16,17,18}
            }
        };

        var students = new List<Student> { a, b, c };

        var out1 = students
                    .Select(x => new { Id = x.Id, Sum = x.Data.SelectMany(n => n).Sum() })
                    .FirstOrDefault(s => s.Id == 1)
                    .Sum;

        var out2 = students
                    .FirstOrDefault(s => s.Id == 1)
                    .Data.SelectMany(n => n)
                    .Sum();

        Console.WriteLine(out1);
        Console.WriteLine(out2);

        Console.ReadLine();
    }
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Option 1 will mean you calculate the sum for potentially all elements just to throw them away. You definitely shouldn't do that option. Find the student first and then sum the data. \$\endgroup\$
    – RobH
    Jun 16, 2022 at 19:11

1 Answer 1

4
\$\begingroup\$

Option 1

students
       .Select(x => new { Id = x.Id, Sum = x.Data.SelectMany(n => n).Sum() })
       .FirstOrDefault(s => s.Id == 1)
       .Sum;
  • .Select: Transforms all Student object in the collection into anonymous objects
  • .FirstOrDefault: Finds the first anonymous object where the Id is matching with the predefined value
  • .Sum: Accesses the Sum property of the anonymous object
    • This is error-prone since FirstOrDefault might return with null if there is no matching element in the collection
    • The fix could be ?.Sum

So, you might need to iterate through twice of the collection (once to transform all elements then if the Id == 1 matches with the last element). So, the order of this function is O(2n).

Option 2

students
       .FirstOrDefault(s => s.Id == 1)
       .Data.SelectMany(n => n)
       .Sum();
  • FirstOrDefault: Finds the first Student object where the Id is matching with the predefined value
  • .Data.SelectMany: Flattens the nested collection of collections to a collection
    • It is error-prone since FirstOrDefault might return with null
  • .Sum(): Aggregates the items in the flatten collection

The order of this function is O(n), since Id == 1 might find the last element.

Alternatives

  • If you would store your Students in a Dictionary then the lookup would be O(1)
Dictionary<int, List<List<int>>> students = ...
var result = students[1].SelectMany(n => n)?.Sum();
  • You could use two Sum calls instead of SelectMany + Sum
var result = students[1].Sum(n => n.Sum());

Please be aware that the indexer operator could throw KeyNotFoundException rather than return with null

\$\endgroup\$
4
  • \$\begingroup\$ "So, the order of this function is O(2n)." That's not how LINQ works. \$\endgroup\$
    – Johnbot
    Jun 16, 2022 at 11:45
  • \$\begingroup\$ @Johnbot Yes, you are correct. Due to the lazy evaluation it will build an expression tree and it might optimise on this when it iterates through the collection. \$\endgroup\$ Jun 16, 2022 at 12:15
  • \$\begingroup\$ @Johnbot It's also possible to check the source-code in github or referencesource.microsoft.com, to know how does LINQ work. (which would give you a good idea on which approach would be feasible to the code). \$\endgroup\$
    – iSR5
    Jun 16, 2022 at 14:54
  • \$\begingroup\$ @Johnbot is correct - the source collection will only be iterated once it is not 2n. \$\endgroup\$
    – RobH
    Jun 16, 2022 at 19:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.