Marko
Marko

Reputation: 227

Linq group by, order by values

I need help I have Table:

   Id          Day                 Group       Value 
 -------------------------------------------------
   1           2016-10-11          1           10.5
   2           2016-10-11          1           10.8
   3           2016-10-11          1           10.7
   4           2016-10-11          1           10.6
   5           2016-10-11          1           10.5
   6           2016-10-11          1           10.4
   7           2016-10-11          1           10.8
   8           2016-10-11          1           10.2
   9           2016-10-11          1           10.0
   10          2016-10-11          1           10.9
   11          2016-10-11          2           10.1
   12          2016-10-11          2           10.0
   13          2016-10-11          2           10.1
   14          2016-10-11          2           10.6
   15          2016-10-11          2           10.7
   16          2016-10-11          2           10.2
   17          2016-10-11          2           10.0
   18          2016-10-11          2           10.5
   19          2016-10-11          2           10.5
   20          2016-10-11          2           10.8
   21          2016-10-12          1           11.1
   22          2016-10-12          1           11.7
   23          2016-10-12          1           11.0
   24          2016-10-12          1           11.4
   25          2016-10-12          1           11.7
   26          2016-10-12          1           11.8
   27          2016-10-12          1           11.1
   28          2016-10-12          1           11.1
   29          2016-10-12          1           11.4
   30          2016-10-12          1           11.6
   31          2016-10-12          2           11.9 
   32          2016-10-12          2           11.6
   ...

And I want

[[[10.5,10.8],[10.0,10.1]],[[11.1,11.7],[11.6,11.9]]] (example)

Respectively I need work with this value more, when I get value like example I get more value in Day. And then I split this batch to 4 batch and then I choose just first value from every group. So I will have 4 value for every Group and for 1 Day I will have 2 batch and every batch will have 4 value. And because I need have 5 value like last value in every batch I find Max Value and add it to batch with 4 value. I don’t know if you understand so there is my code:

    var valueList = await Task.Factory.StartNew(() =>

    (
        from pv in db.Values
        where DbFunctions.TruncateTime(pv.Day) >= startDate      
        && DbFunctions.TruncateTime(pv.Day) <= endDate           
        orderby pv.Value
        //group pv by pv.Day into gpv
        select pv
    ));

var group1 = await Task.Factory.StartNew(() => (
(
    from vl in valueList
    where vl.Group == 1
    select vl) 
));

var group2 = await Task.Factory.StartNew(() =>
(
    from vl in valueList
    where vl.Group == 2
    select vl
));

var group3 = await Task.Factory.StartNew(() =>
(
    from vl in valueList
    where vl.Group == 3
    select vl 
));


var group1Split = group1.Split(group1.Count() / 4 + 1).Select(x => x.First());
var group1Max = group1.Max();
var group2Split = group2.Split(group2.Count() / 4 + 1).Select(x => x.First());
var group2Max = group2.Max();
var group3Split = group3.Split(group3.Count() / 4 + 1).Select(x => x.First());
var group3Max = group3.Max();

var group1Values = group1Split.Concat(group1Max);
var group2Values = group2Split.Concat(group2Max);
var group3Values = group3Split.Concat(group3Max);

var groupAllValues = group1Values.Concat(group2Values).Concat(group3Values);
var groupSplitAllValues = groupAllValues.Split(5);

return Request.CreateResponse(HttpStatusCode.OK, groupSplitAllValues.ToList());

Update:

this work:

var result = db.Values
    .Where(x => (DbFunctions.TruncateTime(x.Day)) >= startDate
    && (DbFunctions.TruncateTime(x.Day)) <= endDate)
   .GroupBy(x => new { Day = x.Day, Group = x.Group })
   .Select(x => x.Select(y => y.Value).OrderBy(z => z).ToList()).ToList();

I get this:

[
[10.0, 10.2, 10.4, 10.5, 10.5, 10.6, 10.7, 10.8, 10.8, 10.9],
[10.0, 10.0, 10.1, 10.1, 10.2, 10.5, 10.5, 10.6, 10.7, 10.8],
[11.0, 11.1, 11.1, 11.1, 11.4, 11.4, 11.6, 11.7, 11.7, 11.8],
[...]
]

But I need split every batch to in this case 2 part and select first value from this part. So I will have 2 value and then I want concat to 2 value max value from this batch so for this example my final result will be:

[
[10.0, 10.6, 10.9],
[10.0, 10.2, 10.8],
[11.0, 11.4, 11.8],
[...]
]

For splitting I use this method:

public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int length)
        {
            if (length <= 0)
                throw new ArgumentOutOfRangeException("length");

            var section = new List<T>(length);

            foreach (var item in source)
            {
                section.Add(item);

                if (section.Count == length)
                {
                    yield return section.AsReadOnly();
                    section = new List<T>(length);
                }
            }

            if (section.Count > 0)
                yield return section.AsReadOnly();
        }

Upvotes: 0

Views: 401

Answers (2)

jdweng
jdweng

Reputation: 34433

Try this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

namespace ConsoleApplication23
{
    class Program
    {
        static void Main(string[] args)
        {

            DataTable dt = new DataTable();
            dt.Columns.Add("ID", typeof(int));
            dt.Columns.Add("Day", typeof(DateTime));
            dt.Columns.Add("Group", typeof(int));
            dt.Columns.Add("Value", typeof(decimal));

            dt.Rows.Add(new object[] {1 , DateTime.Parse("2016-10-11"), 1, 10.5});
            dt.Rows.Add(new object[] {2 , DateTime.Parse("2016-10-11"), 1, 10.8});
            dt.Rows.Add(new object[] {3 , DateTime.Parse("2016-10-11"), 1, 10.7});
            dt.Rows.Add(new object[] {4 , DateTime.Parse("2016-10-11"), 1, 10.6});
            dt.Rows.Add(new object[] {5 , DateTime.Parse("2016-10-11"), 1, 10.5});
            dt.Rows.Add(new object[] {6 , DateTime.Parse("2016-10-11"), 1, 10.4});
            dt.Rows.Add(new object[] {7 , DateTime.Parse("2016-10-11"), 1, 10.8});
            dt.Rows.Add(new object[] {8 , DateTime.Parse("2016-10-11"), 1, 10.2});
            dt.Rows.Add(new object[] {9 , DateTime.Parse("2016-10-11"), 1, 10.0});
            dt.Rows.Add(new object[] {10, DateTime.Parse("2016-10-11"), 1, 10.9});
            dt.Rows.Add(new object[] {11, DateTime.Parse("2016-10-11"), 2, 10.1});
            dt.Rows.Add(new object[] {12, DateTime.Parse("2016-10-11"), 2, 10.0});
            dt.Rows.Add(new object[] {13, DateTime.Parse("2016-10-11"), 2, 10.1});
            dt.Rows.Add(new object[] {14, DateTime.Parse("2016-10-11"), 2, 10.6});
            dt.Rows.Add(new object[] {15, DateTime.Parse("2016-10-11"), 2, 10.7});
            dt.Rows.Add(new object[] {16, DateTime.Parse("2016-10-11"), 2, 10.2});
            dt.Rows.Add(new object[] {17, DateTime.Parse("2016-10-11"), 2, 10.0});
            dt.Rows.Add(new object[] {18, DateTime.Parse("2016-10-11"), 2, 10.5});
            dt.Rows.Add(new object[] {19, DateTime.Parse("2016-10-11"), 2, 10.5});
            dt.Rows.Add(new object[] {20, DateTime.Parse("2016-10-11"), 2, 10.8});
            dt.Rows.Add(new object[] {21, DateTime.Parse("2016-10-12"), 1, 11.1});
            dt.Rows.Add(new object[] {22, DateTime.Parse("2016-10-12"), 1, 11.7});
            dt.Rows.Add(new object[] {23, DateTime.Parse("2016-10-12"), 1, 11.0});
            dt.Rows.Add(new object[] {24, DateTime.Parse("2016-10-12"), 1, 11.4});
            dt.Rows.Add(new object[] {25, DateTime.Parse("2016-10-12"), 1, 11.7});
            dt.Rows.Add(new object[] {26, DateTime.Parse("2016-10-12"), 1, 11.8});
            dt.Rows.Add(new object[] {27, DateTime.Parse("2016-10-12"), 1, 11.1});
            dt.Rows.Add(new object[] {28, DateTime.Parse("2016-10-12"), 1, 11.1});
            dt.Rows.Add(new object[] {29, DateTime.Parse("2016-10-12"), 1, 11.4});
            dt.Rows.Add(new object[] {30, DateTime.Parse("2016-10-12"), 1, 11.6});
            dt.Rows.Add(new object[] {31, DateTime.Parse("2016-10-12"), 2, 11.9});
            dt.Rows.Add(new object[] {32, DateTime.Parse("2016-10-12"), 2, 11.6});

            var results = dt.AsEnumerable()
                .GroupBy(x => new { day = x.Field<DateTime>("Day"), group = x.Field<int>("Group")})
                .Select(x => x.Select(y => y.Field<decimal>("Value")).OrderBy(z => z).ToList()).ToList();

            List<List<decimal>> groups = new List<List<decimal>>();
            const int NUM_GROUPS = 2;
            foreach (var result in results)
            {
                int size = result.Count;
                int groupSize = (int)(size / NUM_GROUPS); //round down

                List<decimal> newGroup = new List<decimal>() { result[0] };
                groups.Add(newGroup);
                for (int groupNum = 0; groupNum < NUM_GROUPS; groupNum++)
                {
                    newGroup.Add( result.Skip(groupNum * groupSize).Take(groupSize).Max());
                }
            }


        }
    }
}

Upvotes: 1

Innat3
Innat3

Reputation: 3576

Here's a sample of how I did it, I am sure that there must be a more elegant way of generating the output string though

static void Main(string[] args)
    {
        List<Example> samples = new List<Example>();
        samples.Add(new Example(1, DateTime.Parse("2016-10-11"), 1, 10.5));
        samples.Add(new Example(2, DateTime.Parse("2016-10-11"), 1, 10.8));
        samples.Add(new Example(3, DateTime.Parse("2016-10-11"), 2, 10.1));
        samples.Add(new Example(4, DateTime.Parse("2016-10-11"), 2, 10.0));
        samples.Add(new Example(5, DateTime.Parse("2016-10-12"), 1, 11.1));
        samples.Add(new Example(6, DateTime.Parse("2016-10-12"), 1, 11.7));
        samples.Add(new Example(7, DateTime.Parse("2016-10-12"), 2, 11.9));
        samples.Add(new Example(8, DateTime.Parse("2016-10-12"), 2, 11.6));

        var daily_results = samples.GroupBy(g => new { g.group, g.date }).GroupBy(g => g.Key.date);

        StringBuilder sb = new StringBuilder();

        sb.Append("[");
        foreach (var group_result in daily_results)
        {
            sb.Append("[");
            foreach(var result in group_result)
            {
                sb.Append($"[{string.Join(",", result.OrderBy(r => r.value).Select(r => r.value))}]");
            }
            sb.Append("]");
        }
        sb.Append("]");

        Console.WriteLine(sb);
        Console.Read();
    }
}
class Example
{
    public int id;
    public DateTime date;
    public int group;
    public double value;
    public Example(int i, DateTime d, int g, double v)
    {
        id = i;
        date = d;
        group = g;
        value = v;
    }
}

UPDATE: Output code can be simplified like this:

        var daily_results = samples.GroupBy(g => new { g.group, g.date }).GroupBy(g => g.Key.date);

        string output = string.Empty;

        daily_results.ToList().ForEach(grp =>
            {
                output += "[";
                grp.ToList().ForEach(res =>
                {
                    output += $"[{string.Join(",", res.OrderBy(r => r.value).Select(r => r.value))}],";
                });
                output = output.TrimEnd(',') + "],";
            });
        Console.WriteLine($"[{output.TrimEnd(',')}]");
        Console.Read();

Upvotes: 0

Related Questions