1

i am having issue grouping object by using linq was wondering if someone can tell me what i am doing wrong here. Also the select function is an extension i grabbed from here (the link) so i can compere the previous and current value, if the value is between a range then i set the value return to the current value.

// Range of values, the first item in the group data, i 
var ranges = new List<double> { 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0 };

        // Simple class i created
        public class CurrencyGroupItemData
        {
            public string Code { get; set; }

            public double TotalStrength { get; set; }
        }

        var lstCurrencyGroups = new List<CurrencyGroupItemData>();

        lstCurrencyGroups.Add(new CurrencyGroupItemData
        { Code = "USD", TotalStrength = 5.0 });
        lstCurrencyGroups.Add(new CurrencyGroupItemData
        { Code = "CHF",TotalStrength = 2.14285714285714 });
        lstCurrencyGroups.Add(new CurrencyGroupItemData
        { Code = "EUR",TotalStrength = 3.85714285714286 });
        lstCurrencyGroups.Add(new CurrencyGroupItemData
        { Code = "GBP",TotalStrength = 3.42857142857143 });
        lstCurrencyGroups.Add(new CurrencyGroupItemData
        { Code = "JPY",TotalStrength = 5.71428571428571 });
        lstCurrencyGroups.Add(new CurrencyGroupItemData
        { Code = "CAD",TotalStrength = 6.85714285714286 });
        lstCurrencyGroups.Add(new CurrencyGroupItemData
        { Code = "AUD",TotalStrength = 4.28571428571429 });
        lstCurrencyGroups.Add(new CurrencyGroupItemData
        { Code = "NZD",TotalStrength = 4.71428571428571 });

// compare The total strength of each object, if it's value is between any of ranges above then group item by the range value. for example if the object totalstrength value is 5.7 that value is between 6.0 and 5.0, it's range value would the minimum of the 2 which 5.0, i would group that item by 5.0. 
var jjjj01 = lstCurrencyGroups.GroupBy(x => ranges.SelectWithPrev((double r1, double r2, bool isfirst)
            => (isfirst && x.TotalStrength >= r1) ? r1 : (x.TotalStrength <= r1 && x.TotalStrength <= r2) ? r2 : 0.0).ToArray())
            .Select(g => new { Rank = g.Key, Count = g.Count() })
            .ToList();

// the extension i grabbed from the link above
public static IEnumerable<TResult> SelectWithPrev<TSource, TResult>
(this IEnumerable<TSource> source, Func<TSource, TSource, bool, TResult> projection)
{
    using (var iterator = source.GetEnumerator())
    {
        var isfirst = true;
        var previous = default(TSource);
        while (iterator.MoveNext())
        {
            yield return projection(iterator.Current, previous, isfirst);
            isfirst = false;
            previous = iterator.Current;
        }
    }
}
DsscSystems
  • 91
  • 1
  • 3
  • 12
  • 4
    Are those truly your ranges, or this is example data for purposes of this post? If those are your ranges, you can just `.GroupBy(x => Math.Floor(x.TotalStrength))` – Michael Gunter Jun 08 '18 at 16:05
  • If those aren't your ranges then just replace `Math.Floor(x.TotalStrength)` in Michael Gunter's comment with another method (possibly one you've written yourself) that converts the strengths to the value you want to group by. – Chris Jun 08 '18 at 16:06
  • yes, these are truely the ranges, the only ranges that it will need to use – DsscSystems Jun 08 '18 at 16:38
  • also the only values that are dynamic are the total strength those could change, the range values will not, i want to group them by range values. – DsscSystems Jun 08 '18 at 16:42
  • Your error is in the test to see if `TotalStrength` is between the range values. – NetMage Jun 08 '18 at 23:42
  • well i need to group based on what range the values fall into, so for example if one was 5.2, and the other 5.3 they would fall between ranges 6.0 and 5.0 and i would group those 2 item together – DsscSystems Jun 09 '18 at 15:59

1 Answers1

0

Using my own extension to pair up the ranges:

public static IEnumerable<TResult> ScanByPairs<T, TResult>(this IEnumerable<T> src, Func<T, T, TResult> combineFn) {
    using (var srce = src.GetEnumerator()) {
        if (srce.MoveNext()) {
            var prev = srce.Current;

            while (srce.MoveNext()) {
                yield return combineFn(prev, srce.Current);
                prev = srce.Current;
            }
        }
    }
}

The answer is pretty straight forward. Convert the ranges into pairs of ranges:

var rangesWithKey = ranges.ScanByPairs((p,c) => (max: p, min: c)).ToList();

Then group by the first pair that contains the TotalStrength.

var ans = lstCurrencyGroups.GroupBy(cg => rangesWithKey.FirstOrDefault(rk => rk.min <= cg.TotalStrength && cg.TotalStrength <= rk.max).min)
                           .Select(cgg => new { Rank = cgg.Key, Count = cgg.Count() })
                           .OrderBy(cgg => cgg.Rank);
NetMage
  • 26,163
  • 3
  • 34
  • 55
  • hi NetMage just tried the code u added, and it worked like charm! Thank you, was trying to figure this out the past 2 days! – DsscSystems Jun 09 '18 at 17:45