Max
Max

Reputation: 43

Grouping list items by class property proximity from each other with some threshold

I need to group objects in proximity to each other by class property. Suppose I want to group my list data by s.Start with a threshold of 3.

data.GroupByProximity(s => s.Start, 3);

The basic class

List<Threat> data = new List<Threat>
{
    new Threat{Name = "1",Source=1, Start=182},
    new Threat{Name = "T2", Source=2,  Start=183},
    new Threat{Name = "5", Source=3, Start=181},
    new Threat{Name = "7",Source=1, Start=282},
    new Threat{Name = "T6", Source=2,  Start=283},
    new Threat{Name = "TT", Source=3, Start=281}
};

I think this is very similar to the post (LINQ (Or pseudocode) to group items by proximity), but I cannot figure out how to apply it in this case where the query is instantiating to ThreatClass.

 public static IEnumerable<IGrouping<TKey, TSource>> GroupByProximity<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, int threshold) where TSource : class
    {
        var g = new List<TSource>();
        foreach (var x in source)
        {
            if ((g.Count != 0) && (x > g[0] + threshold))
            {
                yield return (IGrouping<TKey, TSource>)g;
                g = new List<TSource>();
            }
            g.Add(x);
        }
        yield return (IGrouping<TKey, TSource>)g;
    }

Upvotes: 0

Views: 95

Answers (1)

juharr
juharr

Reputation: 32266

You're close, but here's how you can convert the method from that other question to work with a collection of items given a function that returns the int value to do the comparison with.

static IEnumerable<IEnumerable<T>> GroupByProximity<T>(
    this IEnumerable<T> source,
    Func<T, int> selector,
    int threshold)
{
    var g = new List<T>();
    foreach (var x in source)
    {
        if ((g.Count != 0) && (selector(x) > selector(g[0]) + threshold))
        {
            yield return g;
            g = new List<T>();
        }
        g.Add(x);
    }
    yield return g;
}

It should be noted that this relies on the original list being in order so you'd want to do

var result = data.OrderBy(x => x.Start).GroupByProximity(x => x.Start, 3);

or the ordering could be put into the method

foreach(var x in source.OrderBy(selector))

Upvotes: 1

Related Questions