У меня проблема с группировкой объекта с помощью linq, интересно, может ли кто-нибудь сказать мне, что я здесь делаю неправильно. Также функция выбора - это расширение, которое я взял отсюда (ссылка), чтобы я мог сравнить предыдущее и текущее значение, если значение находится между диапазоном, тогда я устанавливаю значение return на текущее значение.

// 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;
        }
    }
}
1
DsscSystems 8 Июн 2018 в 19:01

1 ответ

Лучший ответ

Используя собственное расширение, чтобы объединить диапазоны в пары:

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;
            }
        }
    }
}

Ответ довольно прост. Преобразуйте диапазоны в пары диапазонов:

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

Затем сгруппируйте по первой паре, содержащей 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);
0
NetMage 8 Июн 2018 в 23:44