0

I need a list that contains a list of lists, that contains a list of lists, that contains a list of objects. And I believe that we can achieve this using Linq. But I don't know-how!

Here I left posted a diagram for a better understanding of what I need.

https://i.gyazo.com/fe52e851024b0b13e6d39eeb533c43f2.png

I have an object:

public class Question
{
    public int ModuleID    { get; set; }
    public int GroupID     { get; set; }
    public int QuestionID     { get; set; }
}

I return a list that may look like this:

List<Question> questionList = new List<Question>();
QuestionList.Add( new Question{ ModuleID = 1, GroupID = 1, QuestionID = 1 } );
QuestionList.Add( new Question{ ModuleID = 2, GroupID = 1, QuestionID = 2 } );
QuestionList.Add( new Question{ ModuleID = 3, GroupID = 2, QuestionID = 3 } );
QuestionList.Add( new Question{ ModuleID = 4, GroupID = 4, QuestionID = 4 } );

What I have tried:

var groupedCustomerList = userList
    .GroupBy(u => u.GroupID)
    .Select(grp => grp.ToList())
    .ToList();

From:

Using Linq to group a list of objects into a new grouped list of list of objects

And I have tried:

var groupedCustomerList = CustomerList.GroupBy(u => u.GroupID)
            .Select(grp =>new { GroupID =grp.Key, CustomerList = grp.ToList()})
            .ToList();

From:

Using LINQ to group a list of objects

Jocadel
  • 3
  • 2

2 Answers2

0

Here's a small console app which I think demos how to get the hierarchical grouping you're after:

internal class Program
{
    public class Module
    {
        public int ModuleID { get; set; }
        public List<Group> Groups { get; set; }
    }

    public class Group
    {
        public int GroupID { get; set; }
        public int ModuleID { get; set; }
        public List<Question> Questions { get; set; }
    }

    public class Question
    {
        public int ModuleID { get; set; }
        public int GroupID { get; set; }
        public int QuestionID { get; set; }
    }

    private static void Main(string[] args)
    {
        var questions = new List<Question>();
        questions.Add(new Question {ModuleID = 1, GroupID = 1, QuestionID = 1});
        questions.Add(new Question {ModuleID = 2, GroupID = 1, QuestionID = 2});
        questions.Add(new Question {ModuleID = 3, GroupID = 2, QuestionID = 3});
        questions.Add(new Question {ModuleID = 4, GroupID = 4, QuestionID = 4});

        var groups = questions
            .GroupBy(q => new {q.GroupID, q.ModuleID})
            .Select(qg => new Group
            {
                GroupID = qg.Key.GroupID,
                ModuleID = qg.Key.ModuleID,
                Questions = qg.ToList()
            })
            .GroupBy(g => g.ModuleID)
            .Select(gg => new Module
            {
                ModuleID = gg.Key,
                Groups = gg.ToList()
            })
            .ToList();
    }
}

The most important line here is probably .GroupBy(q => new {q.GroupID, q.ModuleID}). Grouping by the GroupId along is not enough, because we also need to consider if Questions are in different Modules. e.g. In the example of the above test data this would surface by erroneously grouping together questions #1 and #2 because they both have the same GroupID, ignoring the fact that they are in different Modules. To solve this we create a new anonymous object, containg both the GroupID and ModuleID of the question (new {q.GroupID, q.ModuleID})) and group on that anonymous object instead.

simon-pearson
  • 1,601
  • 8
  • 10
  • Hey! The classes "module" and "group" can be converted into a one Generic class? And if so how you would implement it? – Jocadel Apr 05 '20 at 14:02
0

Get you types correct. Your list has integers but the class has strings. Use IEquatable

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

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Question> questionList = new List<Question>();
            questionList.Add(new Question { ModuleID = "1", GroupID = "1", QuestionID = 1 });
            questionList.Add(new Question { ModuleID = "2", GroupID = "1", QuestionID = 2 });
            questionList.Add(new Question { ModuleID = "3", GroupID = "2", QuestionID = 3 });
            questionList.Add(new Question { ModuleID = "4", GroupID = "4", QuestionID = 4 });
            questionList.Add(new Question { ModuleID = "4", GroupID = "4", QuestionID = 4 });

            List<List<Question>> results = questionList.GroupBy(x => x).Select(x => x.ToList()).ToList();
        }
    }

    public class Question : IEquatable<Question>
    {
        public string ModuleID { get; set; }
        public string GroupID { get; set; }
        public int QuestionID { get; set; }

        public Boolean Equals(Question other)
        {
            return
                (ModuleID == other.ModuleID) &&
                (GroupID == other.GroupID) &&
                (QuestionID == other.QuestionID);
        }
        public override int GetHashCode()
        {
            return (ModuleID + "^" + GroupID + "^" + QuestionID.ToString()).GetHashCode();
        }
    }
}
jdweng
  • 33,250
  • 2
  • 15
  • 20