展平父/子对象链,沿途合并值

时间:2014-10-22 11:11:20

标签: c# algorithm parent-child flatten

我们多年来一直盯着这个问题,浪费了宝贵的时间。

我们有这些对象,让我们称之为组件。我们的应用程序允许您基于现有组件创建新组件

父级的所有值都是“已继承”(读取:可通过component.Parent属性访问)除非覆盖某个值 - 例如你想给它一个新的名字,但保留其余的父值。

// Exiting parent-child object chain
BaseObject      {Id = 1, Name = "Base", Description = "Base description", Notes = "Awesome object", ParentId = null}
ChildObject1    {Id = 2, Name = "", Description = "", Notes = "", ParentId = 1}
ChildObject2    {Id = 3, Name = "Custom Name", Description = "", Notes = "", ParentId = 2}
ChildObject3    {Id = 4, Name = "", Description = "Final object", Notes = "", ParentId = 3}

现在我想从TOP DOWN中将这个变平,使用父项的现有值来表示孩子的任何空值。

// End result after flattening/merging
ChildObject3    {Id = 4, Name = "Custom Name", Description = "Final object", Notes = "Awesome object", ParentId = 3}

值得注意的是:没有子属性,只有孩子知道父母。父母不知道孩子。

如何在没有丑陋while(child.Parent != null)构造的情况下解决此问题,并创建previousComponentcurrentComponent等对象来跟踪设置值。

2 个答案:

答案 0 :(得分:1)

递归DeepCopy方法如何:

public class ComponentClass()
{
   public ComponentClass DeepCopy(ComponentClass comp)
   {
       CopyAllNonEmptyPropertiesTo(comp);
       if (comp.StillHasEmptyProperties())
          return Parent.DeepCopy(comp);
       else
          return comp;
   }
}

答案 1 :(得分:0)

创建数据对象的字典,以便查找父母。

对于您拥有的每个项目,在以下情况下执行父值的递归查找:

  • 您没有其中一个目标属性的值
  • 您有父母

E.g。 (Dump()命令来自LinqPad)

var data = new List<Bob>
{
    new Bob { Id = 1, Name = "Base", Description = "Base description", Notes = "Awesome object", ParentId = null },
    new Bob { Id = 2, Name = "", Description = "", Notes = "", ParentId = 1 },
    new Bob { Id = 3, Name = "Custom Name", Description = "", Notes = "", ParentId = 2 },
    new Bob { Id = 4, Name = "", Description = "Final object", Notes = "", ParentId = 3 },
};
var map = data.ToDictionary(d => d.Id, d => d);

data.ForEach(row => CopyFromParent(map, row, row.ParentId.HasValue ? map[row.ParentId.Value] : null));
data.Dump();

}

void CopyFromParent(Dictionary<int, Bob> map, Bob target, Bob current)
{
    // set properties
    if (current != null && string.IsNullOrEmpty(target.Name) && !string.IsNullOrEmpty(current.Name)) target.Name = current.Name;
    if (current != null && string.IsNullOrEmpty(target.Description) && !string.IsNullOrEmpty(current.Description)) target.Description = current.Description;
    if (current != null && string.IsNullOrEmpty(target.Notes) && !string.IsNullOrEmpty(current.Notes)) target.Notes = current.Notes;

    // dive deeper if we need to, and if we can
    var needToDive = string.IsNullOrEmpty(target.Name) || string.IsNullOrEmpty(target.Description) || string.IsNullOrEmpty(target.Notes);
    var canDive = current != null && current.ParentId.HasValue && map.ContainsKey(current.ParentId.Value);
    if (needToDive && canDive)
    {
        CopyFromParent(map, target, map[current.ParentId.Value]);
    }
}

class Bob
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Notes { get; set; }
    public int? ParentId { get; set; }
}