Santino
Santino

Reputation: 815

How to group a list of integers based on moving range?

I have a

list<int> = {14, 24, 56,189,909,1000};

I want to collapse (group?) them by a range such that the ints that fall within the range of each other are collapse into one value.

So the results should be for range = 100

{14,24,56} //since they 24 falls within 100 of 14 and 56 falls within 100 of 24
{189}
{909, 1000} //since they fall within 100 of each other

I know this is possible using a linq group by but I am stumped by the syntax.

I have looked at this answer but cannot figure out what to use for the ranges, since I have only one range i.e. 100.

int[] values = {100, 110, 120, 130, 140, 150, 160, 170};
        int[] ranges = {115, 145, 180};

    var query = from value in values
                group value by ranges.Where(x => value >= x)
                                     .DefaultIfEmpty()
                                     .Last();

    foreach (var group in query)
    {
        Console.WriteLine("{0}: {{{1}}}", group.Key, 
                          string.Join(", ", group));
    }

Upvotes: 0

Views: 834

Answers (3)

brz
brz

Reputation: 6016

Your best option is using a plain old for loop instead of linq:

            var l = new[] { 14, 24, 56, 189, 909, 1000 };
            var groups = new List<List<int>>();
            groups.Add(new List<int>());
            groups[0].Add(l[0]);
            for (int i = 1; i < l.Length; i++)
            {
                if (l[i] - l[i - 1] > 100)
                {
                    groups.Add(new List<int>());
                }
                groups[groups.Count - 1].Add(l[i]);
            }

Upvotes: 1

user2864740
user2864740

Reputation: 61865

Edit: This may not apply, especially with the additional requirement in the comment as it 1) starts each group only at one point and 2) would place 150 only in the first group.


I would probably write it as so because of the "dynamic" range (and I'm not sure how Group By could be used without an equally involved bucket process). This function requires that the input is already sorted.

IEnumerable<IEnumerable<int>> GroupByStartingRange (IEnumerable<int> src) {
    int? maybeStart;
    while ((maybeStart = src.FirstOrDefault() != null) {
       if (maybeStart.HasValue) {
          var start = maybeStart.Value;
          yield return src.TakeWhile(x => x <= start + 100)
          src = src.SkipWhile(x => x <= start + 100);
       }
    }
}

Upvotes: 1

atlanteh
atlanteh

Reputation: 5835

Assuming your ranges are ordered:

int[] values = { 100, 110, 120, 130, 140, 150, 160, 170 };
int[] ranges = { 115, 145, 180 };
var groups = values.GroupBy(x => ranges.First(r => x <= r));

Upvotes: 0

Related Questions