将列表展开为子对象

Posted

技术标签:

【中文标题】将列表展开为子对象【英文标题】:Unflattening a list to sub-objects 【发布时间】:2018-01-18 08:28:47 【问题描述】:

edit:我意识到我似乎马上就来寻求答案。我试过自己做,但我开始相信有一个我不完全理解的机制。我就是无法解决这个问题!

edit2:我用错词了!对于“父母”和“孩子”,我不使用“DOM”的含义!这是用于我当前开发的 html

<body>
    <h1>FIRST LEVEL TITLE</h1>
    <h4>test</h4>
    <h2>SECOND LEVEL TITLE</h2>
    <h3>THIRD LEVEL TITLE</h3>
    <h4>test</h4>
    <h3>THIRD LEVEL TITLE</h3>
    <h3>THIRD LEVEL TITLE</h3>
    <h2>SECOND LEVEL TITLE</h2>
    <h3>THIRD LEVEL TITLE</h3>
    <h3>THIRD LEVEL TITLE</h3>
    <h4>test</h4>
    <h4>test</h4>
</body>

我要创建的“父”和“子”层次结构是纯虚拟的(仅存在于我的库中,而不存在于 HTML 中)!我的标题标签是 not 嵌套的


使用 Bridge.NET 和 Bridge.JQuery 我已经成功地从 HTML(h1、h2 等)中检索到完整的标题标签列表,并将它们存储到一个平面列表中。 现在我试图给这个列表一个层次结构,所以我列表中的每个元素都有一个属性“Children”,其中包含直接位于它们下方的所有元素,这些子元素包含其他子元素......

如果一个元素与其预期的父元素之间没有中间级元素,则该元素是直接子元素。

在列表中 H2,H3,H4,

H4 是 H3 的孩子,H3 是 H2 的孩子。 但在列表中 H2,H4,H3,

H3 和 H4 是 H2 的子代

示例:

H1 - FIRST LEVEL TITLE
H4 - test
H2 - SECOND LEVEL TITLE
H3 - THIRD LEVEL TITLE
H4 - test
H3 - THIRD LEVEL TITLE
H3 - THIRD LEVEL TITLE
H2 - SECOND LEVEL TITLE
H3 - THIRD LEVEL TITLE
H3 - THIRD LEVEL TITLE
H4 - test
H4 - test

变成

H1 - FIRST LEVEL TITLE
--->H4 - test
--->H2 - SECOND LEVEL TITLE
--->--->H3 - THIRD LEVEL TITLE
--->--->H4 - test
--->--->--->H3 - THIRD LEVEL TITLE
--->--->--->H3 - THIRD LEVEL TITLE
--->H2 - SECOND LEVEL TITLE
--->--->H3 - THIRD LEVEL TITLE
--->--->H3 - THIRD LEVEL TITLE
--->--->H4 - test
--->--->H4 - test

平面列表中的每个标题都定义如下(可以进行任何修改以帮助实现我想要的)

internal class Title

    public TitleLevel Level  get; set; 
    public string Value  get; set; 
    public IEnumerable<Title> Children  get; set;   

    /* removed irrelevant code */


internal enum TitleLevel

    H6,
    H5,
    H4,
    H3,
    H2,
    H1,
    DummyValue // exists for technical reasons, I may ask for advice on that later

【问题讨论】:

如何确定 H4 是 H1 的直接子代,而不是 H2 的直接子代 如果您已经对列表进行了展平,则无法从展平之前获取原始信息。您必须将此信息包含在扁平化列表中。话虽如此,我还不清楚你为什么要把列表压平。 @Marco 编辑了我的问题,检查代码前的新文本 @HimBromBeere 我自己没有把它弄平!我使用jQuery.Select("h1, h2, h3, h4, h5, h6") 检索了它。这给了我一个平面列表。 那么你想要的结果是错误的,因为 H4 和 H2 都不能是 H1 的孩子,因为 H4 的预期父母是 H3 而 H2 不能是直接孩子,因为它的前身是 H4 而不是 H1。此外,您必须假设展平列表的顺序正确。 【参考方案1】:

所以这是我想要的解决方案(取消扁平化列表)

代码确实不漂亮,但它确实有效。

这样称呼

var hierarchyList = GetTitleHierarchy(flatList);

这是定义

/// <summary>
/// Turns a flat list of titles into a list where each element contains a list of it children, themselves containing a list of childre, ...
/// </summary>
/// <param name="titles">A flat list of titles</param>
/// <returns>A "cascading" list of titles</returns>
internal List<Title> GetTitleHierarchy(List<Title> titles)

    return PutInParent(titles);


private List<Title> PutInParent(List<Title> titles)

    var output = new List<Title>();

    for (int i = 0; i < titles.Count; i++)
    
        // Copy because if passed by reference we'll get loop-referencing
        var title = titles.Get(i).Copy();

        var childrenCount = CountChildren(titles, i);
        if (childrenCount > 0)
        
            var subItems = titles.GetRange(i + 1, childrenCount);
            title.Children = PutInParent(subItems);
        

        output.Add(title);
        i += childrenCount;
    

    return output;


/// <summary>
/// Returns the number of titles after the current index that should be children of the current element
/// </summary>
/// <param name="titles">the flat list of elements</param>
/// <param name="startIndex">the current position in the list</param>
/// <returns></returns>
internal int CountChildren(List<Title> titles, int startIndex)

    var clidrenCount = 0;

    foreach (var title in titles.Skip(startIndex + 1))
    
        if (title.IsLevelLowerThan(titles.Get(startIndex).Level))
            break;

        clidrenCount++;
    

    return clidrenCount;


internal class Title

    public TitleLevel Level  get; set; 
    public string Value  get; set; 
    public IEnumerable<Title> Children  get; set; 

    #region Overrides of Object

    public override string ToString()
    
        return $"Level - Value";
    

    #endregion

    public Title Copy()
    
        return new Title
        
            Level = Level,
            Value = Value,
            Children = Children.Select(c => c.Copy())
        ;
    

    public bool IsLevelLowerThan(TitleLevel targetLevel)
    
        return (int) Level <= (int) targetLevel;
    


internal enum TitleLevel

    H1 = 1,
    H2 = 2,
    H3 = 3,
    H4 = 4,
    H5 = 5,
    H6 = 6,
    DummyValue = 0

【讨论】:

以上是关于将列表展开为子对象的主要内容,如果未能解决你的问题,请参考以下文章

在展开可选值 (UIButton) 时意外发现 nil

python 展开嵌套列表

将包含列表项的 dict 展开为 dict 对列表

python中如何将元组展开

react+antd实现列表互不影响的展开每条内容

如何将展开和折叠列表行与 Core Data 元素一起使用?