1

Im tring to make sub-list based on main list with 9 records according to datetime ascending order & then pcName(string).

my sorted sublist need to be like below

ObjectList 1 :

2615,2019-11-22 16:03:22.150,Test1

2615,2019-11-22 16:03:22.200,Test1

2615,2019-11-22 16:03:22.250,Test1

2615,2019-11-22 16:03:22.300,Test1

ObjectList 2 :

2615,2019-11-22 16:03:22.350,Test2

2615,2019-11-22 16:03:22.400,Test2

ObjectList 3 :

2615,2019-11-22 16:03:22.450,Test1

2615,2019-11-22 16:03:22.500,Test1

ObjectList 4 :

2615,2019-11-22 16:03:22.550,Test3

This object list need to take according to sequence ObjectList[0] ,ObjectList[1] ,ObjectList[2] and ObjectList[3]

I mentioned below my sample code

But this only provides me 3 sublist sets(Item1) filter based with Test1, Test2 & Test3 and 9 sublists but i want as above 4 sub lists. where i want to change code ?? Please help me

private void button_Click(object sender, EventArgs e)
        {
            List<Identity> listOfIdentity = new List<Identity>()
            {
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.550"),PcName="Test3"},                              
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.300"), PcName="Test1"},                            
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.350"), PcName="Test2"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.400"), PcName="Test2"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.200"), PcName="Test1"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.500"), PcName="Test1"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.250"), PcName="Test1"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.450"), PcName="Test1"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.150"), PcName="Test1"}              
            };
            List<Identity> sortedIdentity = listOfIdentity.OrderBy(x => x.QCTime).ThenBy(x => x.PcName).ToList(); // here i order by time and then pc name

            var items1 = listOfIdentity.OrderBy(x => x.QCTime).ThenBy(x => x.PcName).GroupBy(x => x.PcName).Select(grp => grp.ToList()).ToList(); //sub list create based with **Test1, Test2 & Test3** (3 sub lists)
            var items2 = listOfIdentity.OrderBy(x => x.QCTime).ThenBy(x => x.PcName).GroupBy(x => new { x.QCTime, x.PcName }).Select(grp => grp.ToList()).ToList(); //sub list create based with each time and pc name (9 sublists)
        }

        class Identity
        {
            public int id { get; set; }
            public DateTime QCTime { get; set; }
            public string PcName { get; set; }
        }
user2015
  • 63
  • 8

3 Answers3

2

Here is my solution using Linq.Aggregate:

private void button_Click(object sender, EventArgs e)
{
    List<Identity> listOfIdentity = new List<Identity>()
    {
        new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.550"), PcName="Test3"},
        new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.300"), PcName="Test1"},
        new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.350"), PcName="Test2"},
        new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.400"), PcName="Test2"},
        new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.200"), PcName="Test1"},
        new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.500"), PcName="Test1"},
        new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.250"), PcName="Test1"},
        new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.450"), PcName="Test1"},
        new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.150"), PcName="Test1"}
    };
    List<Identity> sortedIdentity = listOfIdentity.OrderBy(x => x.QCTime).ThenBy(x => x.PcName).ToList(); // here i order by time and then pc name
    var result = sortedIdentity.Skip(1).Aggregate(new List<List<Identity>> { new List<Identity> { sortedIdentity[0] } }, (lists, curr) =>
    {
        if (curr.PcName == lists.Last()[0].PcName)
            lists.Last().Add(curr);
        else
            lists.Add(new List<Identity> { curr });
        return lists;
    });
}

It iterates over each element and checks, if it's PcName is the same as previous. If it is, it goes on the same list, else new list is created with this element.


EDIT: More elegant solution suggested by Matt.G

    List<Identity> sortedIdentity = listOfIdentity.OrderBy(x => x.QCTime).ThenBy(x => x.PcName).ToList();
    var result = sortedIdentity.Aggregate(new List<List<Identity>>(), (lists, curr) => 
   { 
        if (curr.PcName == lists.LastOrDefault()?.FirstOrDefault()?.PcName)
            lists.Last().Add(curr);
        else
            lists.Add(new List<Identity> { curr });
        return lists;
    });
Piotr L
  • 939
  • 1
  • 6
  • 12
  • 1
    +1. I would avoid the skip using `var result = sortedIdentity.Aggregate(new List>(), (lists, curr) => { if (curr.PcName == lists.LastOrDefault()?.FirstOrDefault()?.PcName)` – Matt.G Dec 02 '19 at 20:00
  • 1
    @Matt.G your solution looks cleaner, thanks. I've added it to the answer – Piotr L Dec 02 '19 at 20:20
  • thank you very much, both answers working perfect for my scenarios. – user2015 Dec 03 '19 at 03:12
0

There is no built-in LINQ method specialized at grouping consecutive elements in one pass. Creating one is not that difficult though. An implementation can be found in this answer, having the signature bellow:

public static IEnumerable<IEnumerable<TSource>> SplitByPredicate<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, TSource, bool> splitPredicate);

You could use it to solve your problem like this:

var sublists = listOfIdentity
    .OrderBy(x => x.QCTime)
    .ThenBy(x => x.PcName)
    .SplitByPredicate((x, y) => x.PcName != y.PcName);

A different implementation can be found here, having the slightly different signature bellow:

public static IEnumerable<List<TSource>> GroupConsecutiveByKey<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector);

...that could be used like this:

var sublists = listOfIdentity
    .OrderBy(x => x.QCTime)
    .ThenBy(x => x.PcName)
    .GroupConsecutiveByKey(x => x.PcName);
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
-1

since you are trying to group by DateTime column and which only varies in MilliSeconds, grouping is not done as per your expectation. I modified your code as below:

class Identity
    {
        public int id { get; set; }
        public DateTime QCTime { get; set; }
        public string PcName { get; set; }

        public string QCTimeForGroup
        {
            get
            {
                if (QCTime.Millisecond <= 300)
                {
                    return "SL1";
                }
                else if (QCTime.Millisecond > 300 && QCTime.Millisecond <= 400)
                {
                    return "SL2";
                }
                else if (QCTime.Millisecond > 400 && QCTime.Millisecond <= 500)
                {
                    return "SL3";
                }

                return "SL4";
            }
        }
    }


private void button_Click(object sender, EventArgs e)
        {
            List<Identity> listOfIdentity = new List<Identity>()
            {
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.550"),PcName="Test3"},                              
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.300"), PcName="Test1"},                            
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.350"), PcName="Test2"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.400"), PcName="Test2"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.200"), PcName="Test1"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.500"), PcName="Test1"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.250"), PcName="Test1"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.450"), PcName="Test1"},
               new Identity() {id= 2615,QCTime=DateTime.Parse("2019-11-22 16:03:22.150"), PcName="Test1"}              
            };
            List<Identity> sortedIdentity = listOfIdentity.OrderBy(x => x.QCTime).ThenBy(x => x.PcName).ToList(); // here i order by time and then pc name

            var items1 = listOfIdentity.OrderBy(x => x.QCTime).ThenBy(x => x.PcName).GroupBy(x => x.PcName).Select(grp => grp.ToList()).ToList(); //sub list create based with **Test1, Test2 & Test3** (3 sub lists)
            var items2 = listOfIdentity.OrderBy(x => x.QCTime).ThenBy(x => x.PcName).GroupBy(x => new { x.QCTime, x.PcName }).Select(grp => grp.ToList()).ToList(); //sub list create based with each time and pc name (9 sublists)
            var items3 = listOfIdentity.OrderBy(x => x.QCTime).ThenBy(x => x.PcName)
                        .GroupBy(x => new { x.QCTimeForGroup, x.PcName }).Select(grp 
                        => grp.ToList()).ToList(); 
        }
sam
  • 1,937
  • 1
  • 8
  • 14