2

I have data with parent child relationships, finding all children for a given parent is straight forward using Linq, however I have problem other way around, finding all of parents when child is provided.

  List<FlatData> elements = new List<FlatData>
          {
   new FlatData {Id = 1, ParentId = NULL ,Text = "Apple"},
   new FlatData {Id = 2, ParentId = 1, Text = "Cat"},
   new FlatData {Id = 3, ParentId = 2, Text = "Dog"},
   new FlatData {Id = 4, ParentId = 3, Text = "Elephant"}
       };

When Id 4 is given I need to able to reverse traverse and find all parents using LINQ (lambda expression).

halfer
  • 19,824
  • 17
  • 99
  • 186
VR1256
  • 1,266
  • 4
  • 28
  • 56

2 Answers2

10

You can use recursion to do something like this:

private IEnumerable<FlatData> FindAllParents(List<FlatData> all_data, FlatData child)
{
    var parent = all_data.FirstOrDefault(x => x.Id == child.ParentId);

    if (parent == null)
        return Enumerable.Empty<FlatData>();

    return new[] {parent}.Concat(FindAllParents(all_data, parent));
}

And use it like this:

int id = 4;

var child = elements.First(x => x.Id == id);

var parents = FindAllParents(elements, child).ToList();

This solution works, but if you have a large data set, then you should consider using a Dictionary<int,FlatData> to make it it faster to fetch a FlatData object given it's Id.

Here is how the method would look like in this case:

private IEnumerable<FlatData> FindAllParents(Dictionary<int,FlatData> all_data, FlatData child)
{
    if(!all_data.ContainsKey(child.ParentId))
        return Enumerable.Empty<FlatData>();

    var parent = all_data[child.ParentId];

    return new[] {parent}.Concat(FindAllParents(all_data, parent));
}

And here is how you would use it:

var dictionary = elements.ToDictionary(x => x.Id); //You need to do this only once to convert the list into a Dictionary

int id = 4;

var child = elements.First(x => x.Id == id);

var parents = FindAllParents(dictionary, child).ToList();
Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62
  • Thank you Yacoub appreicate it , we anticipate large data set for now its 23000 records and this might grow as we add records. can you please post solution for dictionary – VR1256 Jan 11 '16 at 20:49
  • @VijenderReddyChintalapudi, if this answer your question, consider marking it as an answer – Yacoub Massad Feb 16 '16 at 11:14
  • @YacoubMassad your solution works perfect. However, my requirement is the opposite. I need to find all children recursively. How do I accomplish this? – corix010 May 24 '16 at 19:58
  • @corix010, it is really hard to answer this in the comments. Try to do it yourself and if you fail, ask a question. – Yacoub Massad May 24 '16 at 20:42
  • @YacoubMassad I tried modifying your method but could only get all descendants in one branch. Please see my question -> http://stackoverflow.com/questions/37441398/find-all-descendants-in-self-referencing-parent-child-hierarchical-tree – corix010 May 25 '16 at 18:54
4

This works:

var parents = elements.ToDictionary(x => x.Id, x => x.ParentId);

Func<int, IEnumerable<int?>> getParents = null;
getParents = i =>
    parents.ContainsKey(i)
        ? new [] { parents[i] }.Concat(parents[i].HasValue
            ? getParents(parents[i].Value)
            : Enumerable.Empty<int?>())
        : Enumerable.Empty<int?>();

If I ask for getParents(4) then I get this result:

result

A slightly simplified version that removes the null parent is this:

var parents =
    elements
        .Where(x => x.ParentId != null)
        .ToDictionary(x => x.Id, x => x.ParentId.Value);

Func<int, IEnumerable<int>> getParents = null;
getParents = i =>
    parents.ContainsKey(i)
        ? new [] { parents[i] }.Concat(getParents(parents[i]))
        : Enumerable.Empty<int>();
Enigmativity
  • 113,464
  • 11
  • 89
  • 172