Получить родителей для сущности в иерархии - PullRequest
2 голосов
/ 16 ноября 2010

Пытался решить эту проблему без прогресса в течение пары дней, мой вопрос:

Если задана сущность в иерархии, вернуть список ближайших потомков (или родителя s *).1005 *) сущности.

Когда я хочу узнать иерархию «сущности 1.2.2.2», она возвращает список, содержащий только полужирные элементы:

<b>Entity 1</b>
- Entity 1.1
- <b>Entity 1.2</b>
  - Entity 1.2.1
  - <b>Entity 1.2.2</b>
    - Entity 1.2.2.1
    - <b>Entity 1.2.2.2</b>
  - Entity 1.2.3
  - Entity 1.2.4
    - Entity 1.2.4.1
- Entity 1.3
- Entity 1.4
Entity 2
...

Следовательно, ожидаемый результат:

<b>Entity 1</b>
- <b>Entity 1.2</b>
  - <b>Entity 1.2.2</b>
    - <b>Entity 1.2.2.2</b>

Код реализации:

class Entity
{
    public Entity Parent { get; set; }

    public bool AbsoluteParent 
    { 
        get
        { 
            return Parent == null;
        } 
    }

    public IEnumerable<Entity> Hierarchy //problem
    { 
        get
        {
            return AbsoluteParent 
                ? new [] { this }
                : new [] { this }.Concat( Parent.Hierarchy );
        }
    }

}

Вышеприведенная попытка массива является лишь одной из опций, которые я пробовал, этот код на самом деле возвращает что-то вроде:

<b>Entity 1</b>
<b>Entity 1</b>
- <b>Entity 1.2</b>
<b>Entity 1</b>
- <b>Entity 1.2</b>
  - <b>Entity 1.2.2</b>
<b>Entity 1</b>
- <b>Entity 1.2</b>
  - <b>Entity 1.2.2</b>
    - <b>Entity 1.2.2.2</b>

Я могу достичь ожидаемых результатов с помощью функции parents() в jQuery, я читал ключевые слова yield return, но все еще придерживался более функционального стиля (я полагаю), который я все еще в детстве-входя в.

Ответы [ 3 ]

4 голосов
/ 16 ноября 2010

Допустим ли блок итератора?

public IEnumerable<Entity> Hierarchy
{
    // note: buffers results
    get { return UpwardHierarchy.Reverse(); }    
}

public IEnumerable<Entity> UpwardHierarchy
{
    get
    {
        // genuine lazy streaming
        for (var entity = this; entity != null; entity = entity.Parent)
            yield return entity;
    }
}

Другой вариант: использование MoreLinq's Generate:

return MoreEnumerable.Generate(this, entity => entity.Parent)
                     .TakeWhile(entity => entity != null)
                     .Reverse();
2 голосов
/ 17 ноября 2010

Поскольку @Ani комментирует свой ответ, нет способа получить ленивую иерархию от корня.

Существует интересная таблица способа выполнения , в которой метод "Reverse ()" отображается как "Отложенное не потоковое выполнение".Таким образом, он оценивается только при доступе к его первому элементу (корню), но ему нужно прочитать всю иерархию UpwardHierarchy, чтобы получить этот первый элемент.

Если вы хотите повысить производительность, вам следует обратить внимание на способ linq (и любой другой).метод "yield return") создает IEnumerable <>.Если вы собираетесь выполнять итерацию по иерархии только один раз, то это нормально, чтобы игнорировать это предупреждение, но если вы собираетесь искать элементы иерархии несколько раз, было бы неплохо вызвать ToList (), чтобы избежать повторной обработки как UpwardHierarchy, так и "Reverse ()".".

Некоторые коды:

[TestClass]
public class HierarchyTest
{
    #region Ani's code
    class Entity
    {
        public Entity Parent { get; set; }

        public IEnumerable<Entity> Hierarchy
        {
            // note: buffers results
            get { return UpwardHierarchy.Reverse(); }
        }

        public int YieldCount = 0;//modified
        public IEnumerable<Entity> UpwardHierarchy
        {
            get
            {
                // genuine lazy streaming
                for (var entity = this; entity != null; entity = entity.Parent)
                {
                    YieldCount++;//modified
                    yield return entity;
                }
            }
        }
    }
    #endregion


    [TestMethod]
    public void TestMethod1()
    {
        /*
        Entity 1
        - Entity 1.2
          - Entity 1.2.2
            - Entity 1.2.2.2
         */
        var e1 = new Entity();
        var e12 = new Entity() { Parent = e1 };
        var e122 = new Entity() { Parent = e12 };
        var e1222 = new Entity() { Parent = e122 };

        var hierarchy = e1222.Hierarchy;
        Assert.AreEqual(0, e1222.YieldCount);//nothing was evaluated until now
        hierarchy.First();
        Assert.AreEqual(4, e1222.YieldCount);//the entire UpwardHierarchy has been yielded to get the first Hierarchy item
        hierarchy.First();
        Assert.AreEqual(8, e1222.YieldCount);//yielded all UpwardHierarchy  itens again to get the first Hierarchy item

        List<Entity> evaluatedHierarchy = e1222.Hierarchy.ToList();//calling ToList() produces a List<Entity> instance so UpwardHierarchy and Reverse() are evaluated only once
        Assert.AreEqual(12, e1222.YieldCount);//Yieldcount+=4 because of ToList()
        evaluatedHierarchy.First();
        Assert.AreEqual(12, e1222.YieldCount);//and now you can use evaluatedHierarchy as you wish without causing another UpwardHierarchy and Reverse() call.
        evaluatedHierarchy.First();
        Assert.AreEqual(12, e1222.YieldCount);
    }
}
1 голос
/ 16 ноября 2010

Не очень LINQ способ сделать это (мой LINQ все еще довольно новый):

List<Entity> graph = new List<Entity>()
Entity current = this;

while (current != null) {
   graph.Add(current);
   current = current.Parent;
}

graph.Reverse();

return graph;
...