Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I group by the difference between rows in a column with linq and c#?

Tags:

c#

linq

group-by

I want to create a new group when the difference between the values in rows are greater then five.

Example:

int[] list = {5,10,15,40,45,50,70,75};

should give me 3 groups:

1,[ 5,10,15 ]
2,[40,45,50]
3,[70,75]

Is it possible to use Linq here?

Thx!

like image 319
Weissvonnix Avatar asked Jul 27 '16 14:07

Weissvonnix


People also ask

Can we use group by in LINQ C#?

You can also use Into Group with GroupBy in VB.Net. LINQ query is ended with the help Select or Groupby clause. It can also support method syntax in both C# and VB.Net languages.

What is GroupBy in LINQ C#?

LINQ GroupBy() Method In LINQ, the GroupBy operator is used to groupin the list/collection items based on the specified value of the key, and it returns a collection of IGrouping<key, Values>. The GroupBy method in LINQ is the same as the SQL group by statement.

What does LINQ Group by return?

GroupBy & ToLookup return a collection that has a key and an inner collection based on a key field value. The execution of GroupBy is deferred whereas that of ToLookup is immediate. A LINQ query syntax can be end with the GroupBy or Select clause.

Is LINQ fast or slow?

It is slightly slower LINQ syntax is typically less efficient than a foreach loop. It's good to be aware of any performance tradeoff that might occur when you use LINQ to improve the readability of your code. And if you'd like to measure the performance difference, you can use a tool like BenchmarkDotNet to do so.


2 Answers

Exploiting side effects (group) is not a good practice, but can be helpful:

  int[] list = { 5, 10, 15, 40, 45, 50, 70, 75 };

  int step = 5;
  int group = 1;

  var result = list
    .Select((item, index) => new {
               prior = index == 0 ? item : list[index - 1],
               item = item,
             })
    .GroupBy(pair => Math.Abs(pair.prior - pair.item) <= step ? group : ++group, 
             pair => pair.item);

Test:

  string report = string.Join(Environment.NewLine, result
    .Select(chunk => String.Format("{0}: [{1}]", chunk.Key, String.Join(", ", chunk))));

Outcome:

1: [5, 10, 15]
2: [40, 45, 50]
3: [70, 75]
like image 136
Dmitry Bychenko Avatar answered Nov 15 '22 05:11

Dmitry Bychenko


Assuming collection has an indexer defined, can be something like this:

const int step = 5;
int currentGroup = 1;
var groups = list.Select((item, index) =>
{
    if (index > 0 && item - step > list[index - 1])
    {
        currentGroup++;
    }
    return new {Group = currentGroup, Item = item};
}).GroupBy(i => i.Group).ToList();
like image 45
Aleksey L. Avatar answered Nov 15 '22 04:11

Aleksey L.