patterncsharpMinor
Recursive method turning flat structure to recursive
Viewed 0 times
methodflatturningrecursivestructure
Problem
I often end up working with systems where the data is delivered with some
Now, the code below is merely a suggestion in how to solve these kind of "problems", I've created the
I am aware of some Linq alternatives to solve the
```
private static List FillRecursive(List flatObjects, int parentId)
{
return flatObjects.Where(x => x.ParentId.Equals(parentId)).Select(item => new Recursiv
Id property and possibly a parentId prop, but never do anyone tempt to actually give me a nice recursive object to play with.Now, the code below is merely a suggestion in how to solve these kind of "problems", I've created the
FlatObject to simulate an incoming with data.class Program
{
static void Main(string[] args)
{
// Fill list with sample data
List flatObjects = new List
{
new FlatObject(1,0),
new FlatObject(2,0),
new FlatObject(3,0),
new FlatObject(4,0),
new FlatObject(5,1),
new FlatObject(6,2),
new FlatObject(7,6),
new FlatObject(8,6)
};
// call the recursive method
var recursiveObjects = FillRecursive(flatObjects, 0);
}
private static List FillRecursive(List flatObjects, int parentId)
{
List recursiveObjects = new List();
foreach (var item in flatObjects.Where(x => x.ParentId.Equals(parentId)))
{
recursiveObjects.Add(new RecursiveObject
{
Id = item.Id,
ParentId = item.ParentId,
Children = FillRecursive(flatObjects, item.Id)
});
}
return recursiveObjects;
}
}
public class FlatObject
{
public int Id { get; set; }
public int ParentId { get; set; }
public FlatObject(int id, int parentId)
{
Id = id;
ParentId = parentId;
}
}
public class RecursiveObject
{
public int Id { get; set; }
public int ParentId { get; set; }
public List Children { get; set; }
}I am aware of some Linq alternatives to solve the
foreach loop but that does hardly not change the approach of this.```
private static List FillRecursive(List flatObjects, int parentId)
{
return flatObjects.Where(x => x.ParentId.Equals(parentId)).Select(item => new Recursiv
Solution
Below is the code for my approach. A benefit is that the heirarchy goes both ways; up and down. While the parent object contains a list of child objects, each child has a reference to the parent object.
Some differences from your original setup:
The gist of this approach is that we first convert the FlatObjects to RecursiveObjects and store them in a dictionary keyed by the Id. Then, we do another pass over the FlatObjects in order to assign the Parent and Children properties of each RecursiveObject, using the dictionary to perform the necessary look-ups with the FlatObject's Id and ParentId properties.
EDIT: Added an "improved" version of my original approach, shown below. The following implementation provides the following benefits:
-
My setup code includes a second root (
```
class FlatObject
{
public int Id { get; set; }
public int? ParentId { get; set; }
public FlatObject(int id)
{
this.Id = id;
}
public FlatObject(int id, int parentId)
: this(id)
{
this.ParentId = parentId;
}
}
class RecursiveObject
{
public int Id { get; private set; }
public RecursiveObject Parent { get; private set; }
private List _Children;
public IEnumerable Children
{
get
{
IEnumerable value = _Children;
if (value == null)
value = EmptyEnumerable.Default;
return value;
}
}
public RecursiveObject(int id)
{
this.Id = id;
}
public void AddChild(RecursiveObject child)
{
if (_Children == null)
_Children = new List();
_Children.A
Some differences from your original setup:
- The ParentId property on FlatObject is nullable, so objects that have no parent will have null.
- The Children property on RecursiveObject is IEnumerable instead of List. This prevents consumers from modifying the contents of the list. If you want to allow consumers to add/remove items from the list, then RecursiveObject should expose methods to perform those actions so that you can make sure that the Parent property is properly assigned when a child is added/removed.
- I've made the Id, Parent, and Children properties on RecursiveObject read-only. This certainly isn't necessary, but you can do that if desired. This is the reason why I made the FlatObjectsToRecursiveObjects method as a static method of the RecursiveObjects class; so that it can have access to the private property setters.
The gist of this approach is that we first convert the FlatObjects to RecursiveObjects and store them in a dictionary keyed by the Id. Then, we do another pass over the FlatObjects in order to assign the Parent and Children properties of each RecursiveObject, using the dictionary to perform the necessary look-ups with the FlatObject's Id and ParentId properties.
class FlatObject
{
public int Id { get; set; }
public int? ParentId { get; set; }
}
class RecursiveObject
{
public int Id { get; private set; }
public RecursiveObject Parent { get; private set; }
public IEnumerable Children { get; private set; }
public static IEnumerable FlatObjectsToRecursiveObjects(IEnumerable flatObjects)
{
// convert flat objects into heirarchial objects and store them in a dictionary keyed with the object's Id
var recursiveObjectsById = flatObjects.Select(item => new RecursiveObject { Id = item.Id }).ToDictionary(item => item.Id);
// group flat objects by their ParentId
var flatObjectsGroupedByParentId = flatObjects.Where(item => item.ParentId.HasValue).GroupBy(item => item.ParentId.Value);
foreach (var group in flatObjectsGroupedByParentId)
{
// find each group's parent object
RecursiveObject parent;
if (recursiveObjectsById.TryGetValue(group.Key, out parent))
{
// convert the group of flat objects to a list of heirarchial objects by looking up the correct heirarchial object from the dictionary
parent.Children = group.Select(item => recursiveObjectsById[item.Id]).ToList();
// assign the parent object to each child object
foreach (var child in parent.Children)
{
child.Parent = parent;
}
}
else
{
// something's wrong!!! ParentId refers to a non-existant object.
}
}
return recursiveObjectsById.Values;
}
}EDIT: Added an "improved" version of my original approach, shown below. The following implementation provides the following benefits:
- The Children property on RecursiveObject never returns a null, but it still retains the "benefit" of lazy initialization. The Children property getter checks if the _Children field is null, and if so it returns the default instance of the
EmptyEnumerableclass (code included).
- Children can now be added/removed using the new AddChild, RemoveChild, and AddChildren methods on RecursiveObject.
- The FlatObjectsToRecursiveObjects method is slightly simpler now because it utilizes the new AddChildren method.
- The FlatObjectsToRecursiveObjects method no longer has to be a member of the RecursiveObject class, since it does not access any private details of the class.
-
My setup code includes a second root (
new FlatObject(9,-1)) and circular references (new FlatObject(10,10) and new FlatObject(0,2)), just to verify that the implementation can handle these special cases.```
class FlatObject
{
public int Id { get; set; }
public int? ParentId { get; set; }
public FlatObject(int id)
{
this.Id = id;
}
public FlatObject(int id, int parentId)
: this(id)
{
this.ParentId = parentId;
}
}
class RecursiveObject
{
public int Id { get; private set; }
public RecursiveObject Parent { get; private set; }
private List _Children;
public IEnumerable Children
{
get
{
IEnumerable value = _Children;
if (value == null)
value = EmptyEnumerable.Default;
return value;
}
}
public RecursiveObject(int id)
{
this.Id = id;
}
public void AddChild(RecursiveObject child)
{
if (_Children == null)
_Children = new List();
_Children.A
Code Snippets
class FlatObject
{
public int Id { get; set; }
public int? ParentId { get; set; }
}
class RecursiveObject
{
public int Id { get; private set; }
public RecursiveObject Parent { get; private set; }
public IEnumerable<RecursiveObject> Children { get; private set; }
public static IEnumerable<RecursiveObject> FlatObjectsToRecursiveObjects(IEnumerable<FlatObject> flatObjects)
{
// convert flat objects into heirarchial objects and store them in a dictionary keyed with the object's Id
var recursiveObjectsById = flatObjects.Select(item => new RecursiveObject { Id = item.Id }).ToDictionary(item => item.Id);
// group flat objects by their ParentId
var flatObjectsGroupedByParentId = flatObjects.Where(item => item.ParentId.HasValue).GroupBy(item => item.ParentId.Value);
foreach (var group in flatObjectsGroupedByParentId)
{
// find each group's parent object
RecursiveObject parent;
if (recursiveObjectsById.TryGetValue(group.Key, out parent))
{
// convert the group of flat objects to a list of heirarchial objects by looking up the correct heirarchial object from the dictionary
parent.Children = group.Select(item => recursiveObjectsById[item.Id]).ToList();
// assign the parent object to each child object
foreach (var child in parent.Children)
{
child.Parent = parent;
}
}
else
{
// something's wrong!!! ParentId refers to a non-existant object.
}
}
return recursiveObjectsById.Values;
}
}class FlatObject
{
public int Id { get; set; }
public int? ParentId { get; set; }
public FlatObject(int id)
{
this.Id = id;
}
public FlatObject(int id, int parentId)
: this(id)
{
this.ParentId = parentId;
}
}
class RecursiveObject
{
public int Id { get; private set; }
public RecursiveObject Parent { get; private set; }
private List<RecursiveObject> _Children;
public IEnumerable<RecursiveObject> Children
{
get
{
IEnumerable<RecursiveObject> value = _Children;
if (value == null)
value = EmptyEnumerable<RecursiveObject>.Default;
return value;
}
}
public RecursiveObject(int id)
{
this.Id = id;
}
public void AddChild(RecursiveObject child)
{
if (_Children == null)
_Children = new List<RecursiveObject>();
_Children.Add(child);
child.Parent = this;
}
public bool RemoveChild(RecursiveObject child)
{
if (_Children != null)
{
bool removed = _Children.Remove(child);
if (removed)
child.Parent = null;
return removed;
}
else
{
return false;
}
}
public void AddChildren(IEnumerable<RecursiveObject> children)
{
if (children == null)
throw new ArgumentNullException("children");
if (_Children == null)
_Children = new List<RecursiveObject>(children);
else
_Children.AddRange(children);
foreach (var child in children)
{
child.Parent = this;
}
}
}
class Program
{
public static IEnumerable<RecursiveObject> FlatObjectsToRecursiveObjects(IEnumerable<FlatObject> flatObjects)
{
// convert flat objects into heirarchial objects and store them in a dictionary keyed with the object's Id
var recursiveObjectsById = flatObjects.Select(item => new RecursiveObject(item.Id)).ToDictionary(item => item.Id);
// group flat objects by their ParentId
var flatObjectsGroupedByParentId = flatObjects.Where(item => item.ParentId.HasValue).GroupBy(item => item.ParentId.Value);
foreach (var group in flatObjectsGroupedByParentId)
{
// find each group's parent object
RecursiveObject parent;
if (recursiveObjectsById.TryGetValue(group.Key, out parent))
{
// convert the group of flat objects to a list of heirarchial objects by looking up the correct heirarchial object from the dictionary
parent.AddChildren(group.Select(item => recursiveObjectsById[item.Id]));
}
else
{
// something's wrong!!! ParentId refers to a non-existant object.
}
}
return recursiveObjectsById.Values;
}
static void Main(string[] args)
{
Context
StackExchange Code Review Q#7032, answer score: 6
Revisions (0)
No revisions yet.