0

I am trying to flatten a parent/child list, of the same <Person> type. The difficulty here, is that I would like to merge both the parents and children into a flat <Person> list.

Closest I got was:

class Person
{
    public string Name { get; set; }
    public List<Person> Children { get; set; }
}

List<Person> parents = new List<Person>() {
    new Person() {
        Name = "parentA",
        Children = new List<Person>() {
            new Person() { Name = "childB" },
            new Person() { Name = "childC" }
        }
    },
    new Person() {
        Name = "parentD",
        Children = new List<Person>() {
            new Person() { Name = "childE" },
            new Person() { Name = "childF" }
        }
    }
};

var result = parents.SelectMany(parent => parent.Children
    .Select(child => parent.Name + ", " + child.Name));

Which gives me the result:

parentA, childB
parentA, childC
parentD, childE
parentD, childF

What I'm looking for is to get a <Person> list such as:

parentA
childB
childC
parentD
childE
childF

Keeping the order, or the performance is not important. But I would like, if possible, to stick to pure LINQ and LINQ methods.

Thanks,

Blah
  • 43
  • 8
  • 2
    https://stackoverflow.com/questions/11830174/how-to-flatten-tree-via-linq – Wiktor Zychla Apr 12 '21 at 15:31
  • 2
    `SelectMany(parent => parent.Children.Append(parent).Select(x => x.Name))` if you only need to go one level deep? – juharr Apr 12 '21 at 15:35
  • 1
    If you want to preserve the order that the parent is first, then children, as in your question, you could use `Prepend` instead of `Append`. `parents.SelectMany(p => p.Children.Prepend(p));` – Michal Rosenbaum Apr 12 '21 at 15:44
  • You guys got it, that's exactly what I'm looking for. Thanks! Can I mark a comment as the solution? – Blah Apr 12 '21 at 16:43

4 Answers4

1

I suggest Concat in combination with SelectMany if you want just one level deep:

 var parents = ...

 var result = parents
   .SelectMany(person => new Person[] {person}.Concat(person.Children))
   .Select(person => person.Name); // if we want just names

 // Let's have a look
 Console.WriteLine(string.Join(Environment.NewLine, result));
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • Dmitry, your answer was very helpful as well, because somewhere in the code, .Prepend() would come out as not being a method on an ObservableCollection<> for some reason (still haven't figured that one out yet). This worked flawlessly, however. Thanks. – Blah Apr 14 '21 at 21:24
1

Thanks to the comments on my initial post, I was able to get what I was looking for.

The solution only works on one level, which is what I needed. It could be made recursive to go deeper though:

var result = parents.SelectMany(person => person.Children
        .Prepend(person))
    .Select(p => p.Name);

Gave me the expected result:

parentA 
childB 
childC 
parentD 
childE 
childF 

Which then allowed me to use all the objects in the flattened lists, including the parent objects.

Blah
  • 43
  • 8
0
var persons = new List<Person>();
parents.ForEach(p => {
        persons.Add(p); 
        persons.AddRange(p.Children);
    }
);


foreach(var person in persons)
        Console.WriteLine(person.Name);
    }

I understand that this isnt a single line but this I suppose is what you are looking for.

shahkalpesh
  • 33,172
  • 3
  • 63
  • 88
0

Try following class :

    class Person
    {
        public static DataTable dt { get;set;}

        public string Name { get; set; }
        public List<Person> Children { get; set; }

        public void GetTree(Person root)
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("Parent Name", typeof(string));
            dt.Columns.Add("Child Name", typeof(string));
            GetTreeRecursive(root, "");

        }
        public void GetTreeRecursive(Person person, string parentName)
        {
            foreach(Person child in person.Children)
            {
                dt.Rows.Add(parentName, child.Name);
                if (child.Children != null)
                {
                    GetTreeRecursive(child, child.Name);
                }
            }
        }
    }
jdweng
  • 33,250
  • 2
  • 15
  • 20