0

I had to group certain records based on 3 Key values , and then sum up other values in a group . But later realized that the performance of LINQ is lesser than for each . SO please help convert the linq code to normal for each

    List<Test> testList= new List<Test>();
    testList.GroupBy(ab => new
    {
     ab.Property1,
     ab.Property2,
     ab.Property3                         
    }).Select(a => new Test
            {
             Property1= a.Key.Property1,
             Property2= a.Key.Property2,
             Property3= a.Key.Property3,               
             Property4= a.Select(ab => ab.Property4).FirstOrDefault(),
             Property5= a.Sum(ab => ab.Property5),
             Property6= a.Sum(ab => ab.Property6),
             Property7= a.Sum(ab => ab.Property7),
             Property8= a.Sum(ab => ab.Property8),
            });
ashwinrajagopal
  • 27
  • 1
  • 1
  • 5
  • Performance vs time. How many times are you executing this? Is it possible to cache it? – Jeroen van Langen May 09 '17 at 07:04
  • This list is being fetched from database which is executed within a for loop (10k) times . Have to convert the linq stuff to some sorting logic using for each loop – ashwinrajagopal May 09 '17 at 07:12
  • 1
    Linq is only a bit slower than foreach, how much do you need performance over readable code? – EpicKip May 09 '17 at 07:13
  • Linq performance is lower than normal for each loop . I just want to convert the same to analyze the difference . – ashwinrajagopal May 09 '17 at 07:15
  • It is highly improbable that *that* code will be slower in LINQ than in foreach. The difference (if present) will be negligible. And it will be much longer to write :-) – xanatos May 09 '17 at 07:15
  • 5
    If this list is fetched from a database, you should let the database do the grouping. Grouping the data (with or wihout linq) local is much much slower than the database doing the selection serverside _(which it is made for)_. So there is your performance issue.. not linq. – Jeroen van Langen May 09 '17 at 07:15
  • @JeroenvanLangen What you said is correct . I agree . But I have limitations to play around Database changing the already existing queries , and i have to do the logic stuff in server side itself . – ashwinrajagopal May 09 '17 at 07:24
  • Please help to group to list based on multiple keys WITHOUT USING LINQ C# – ashwinrajagopal May 09 '17 at 07:28

2 Answers2

1

Try below link for conversion. http://www.sqltolinq.com/

UPDATE

You can also use Resharper for this as per folowing link. How to get Resharper to convert back to a foreach loop

Community
  • 1
  • 1
SynozeN Technologies
  • 1,337
  • 1
  • 14
  • 19
0

Line by line it should be:

List<Test> testList = new List<Test>();

// string, string, string = Property1, Property2, Property3
var dict = new Dictionary<Tuple<string, string, string>, List<Test>>();

foreach (var el in testList)
{
    List<Test> list;

    var key = Tuple.Create(el.Property1, el.Property2, el.Property3);

    if (!dict.TryGetValue(key, out list))
    {
        list = new List<Test>();
        dict.Add(key, list);
    }

    list.Add(el);
}

var output = new List<Test>(dict.Count);

foreach (var kv in dict)
{
    var list = kv.Value;

    var el = new Test
    {
        Property1 = kv.Key.Item1,
        Property2 = kv.Key.Item2,
        Property3 = kv.Key.Item3,
        Property4 = list[0].Property4,
    };

    output.Add(el);

    for (int i = 0; i < list.Count; i++)
    {
        el.Property5 += list[i].Property5;
        el.Property6 += list[i].Property6;
        el.Property7 += list[i].Property7;
        el.Property8 += list[i].Property8;
    }
}

The only "real" advantage here is that the inner for cycle for the Sum part is a single for instead of being the four separate for used by the four separate Sum.

But there is another way to do it, that is different from LINQ...

List<Test> testList = new List<Test>();

// string, string, string = Property1, Property2, Property3
var dict = new Dictionary<Tuple<string, string, string>, Test>();

foreach (var el in testList)
{
    Test el2;

    var key = Tuple.Create(el.Property1, el.Property2, el.Property3);

    if (!dict.TryGetValue(key, out el2))
    {
        el2 = new Test
        {
            Property1 = el.Property1,
            Property2 = el.Property2,
            Property3 = el.Property3,
            Property4 = el.Property4,
        };

        dict.Add(key, el2);
    }

    el2.Property5 += el.Property5;
    el2.Property6 += el.Property6;
    el2.Property7 += el.Property7;
    el2.Property8 += el.Property8;
}

var output = dict.Values.ToList();

Here we unify the two foreach cycles and we remove the inner for cycle.

Now, unless you are working on million of records, I don't think the difference will amount to much for both solutions.

Note that there is an important difference in output between my code and the LINQ code: the GroupBy operator, when used on a IEnumerable, guarantees the ordering of the groups to be the same as in the input (so the first element will generate the first group, the next element with a different key will generate the second group and so on). Using a Dictionary<,> this doesn't happen. The ordering of the output isn't defined and will be "random".

xanatos
  • 109,618
  • 12
  • 197
  • 280