-3

I have and object called Person that has props

string Name string Amount 

The amount is an integer but held as a string.

Each group is formed when the Amount is within n of each other.

i.e. if n=3

Jeff 20 Jack 19 Ben 16 Kyle 12 would be 3 groups Jeff 20 Jack 19 ---------- Ben 16 ---------- Kyle 12 ---------- 

I tried this but No grouping happens...

var ranges = new[] {3}; var grouped = orderByResult.GroupBy(x => ranges.FirstOrDefault(r => r > Convert.ToInt32(x.Amount))); 
6
  • 1
    umm, curious question, what does the criteria to group them into 3? or just take whatever elements and make group of three? (if yes, you can see this QA) Commented Sep 17, 2019 at 1:10
  • 1
    what have you tried already? what if you have element which is within 3 of two other groups? let's say a range 1-10 - how should they be grouped? Commented Sep 17, 2019 at 1:10
  • 2
    Possible duplicate of Group by variable integer range using Linq Commented Sep 17, 2019 at 1:10
  • 3 has no meaning just an example. Simple want to group by a single range Commented Sep 17, 2019 at 1:13
  • 1
    16 is within 3 of 19. Why it is not in the same group? Commented Sep 17, 2019 at 2:11

2 Answers 2

1

The logic for generating these groups could be interpreted as follows:

  1. Sort the list
  2. Go through the list one by one and compute the gap between the current item and the previous item
  3. When the gap is less than 3, continue the group; otherwise start a new group

This can be coded like this:

static IEnumerable<IEnumerable<Item>> GroupUp(IEnumerable<Item> input) { var sorted = input.OrderBy( item => int.Parse(item.Amount) ).GetEnumerator(); int lastValue = int.MinValue; var list = new List<Item>(); while (sorted.MoveNext()) { var current = sorted.Current; var currentAmount = int.Parse(current.Amount); var gap = currentAmount - lastValue; if (gap < 3) { list.Add(current); } else { if (list.Count != 0) yield return list; list = new List<Item>(); list.Add(current); } lastValue = currentAmount; } if (list.Count != 0) yield return list; } 

See my working example on DotNetFiddle.

Sign up to request clarification or add additional context in comments.

1 Comment

Exactly. Thank You Sir!
1

You can use the answer from this link https://stackoverflow.com/a/1376305/2770274

You just need to write your own code to generate ceilings. It can look for example like this:

IEnumerable<int> GetRangeCeilings(IEnumerable<Item> input) { const int maximumAllowedDistance = 1; var amounts = input.Select(x => int.Parse(x.Amount)).OrderBy(x => x).ToArray(); for (var i = 1; i < amounts.Length; i ++) { if (amounts[i - 1] < amounts[i] - maximumAllowedDistance) { yield return amounts[i - 1]; } } yield return int.MaxValue; } 

now with the previous answer:

var items = new Item[] { new Item { Name = "Jeff", Amount = "20" }, new Item { Name = "Jack", Amount = "19" }, new Item { Name = "Ben", Amount = "16" }, new Item { Name = "Kyle", Amount = "12" } }; var ceilings = GetRangeCeilings(items).ToArray(); var groupings = items.GroupBy(item => ceilings.First(ceiling => ceiling >= int.Parse(item.Amount))); 

Comments