How to Target a Specific Group of Siblings in a Flat Hierarchy

How can I target a specific group of siblings in a flat hierarchy?

Due to the way the general sibling combinator works, it is not possible to limit a selection of siblings to a specific range or group, even of consecutive siblings. Even the :not() selector won't be of any help here.

You will have to use JavaScript to target the right elements. jQuery's .nextUntil() method immediately springs to mind.

CSS General Sibling Selector Specificity

Both selectors have the same specificity, what causes h2 ~ p to take precedence is that it is defined after, therefore cascading over h1 ~ p. How close the sibling are is of no consequence.

For the behavior you want you can use the adjacent sibling selector +.

If you change the h1 ~ p after you will see it takes precidience

h2 ~ p {    color: green;}
h1 ~ p { color: red;}
<h1>Section 1</h1><p>I came after a h1 and should be red.</p>    <h2>Section 2</h2><p>I came after a h2 and should be green.</p>
<h1>Section 3</h1><p>I should be the same color as the section one text?</p>

Sibling css selector not changing

Given html at Question you can use adjacent sibling selector +, !important

[class="1"],[class="1"] ~ tr {  background-color: blue;}
[class="2"],[class="2"] + tr { background-color: red !important;}
<table>  <tbody>    <tr class="1">      <td>foo</td>    </tr>    <tr>      <td>foo</td>    </tr>    <tr>      <td>foo</td>    </tr>    <tr class="2">      <td>foo</td>    </tr>    <tr>      <td>foo</td>    </tr>    <tr class="1">      <td>foo</td>    </tr>    <tr>      <td>foo</td>    </tr>    <tr>      <td>foo</td>    </tr>    <tr>      <td>foo</td>    </tr>  </tbody></table>

Building hierarchy objects from flat list of parent/child

Here's the function I ended up writing. I'm using MPTT to store objects, so the list is in order of the 'left' value, which basically means the parent always comes before any given item in the list. In other words, the item referenced by item.ParentID has always already been added (except in the case of top-level or root nodes).

public class TreeObject
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Name { get; set; }
public IList<TreeObject> Children { get; set; } = new List<TreeObject>();
}

public IEnumerable<TreeObject> FlatToHierarchy(List<TreeObject> list)
{
// hashtable lookup that allows us to grab references to containers based on id
var lookup = new Dictionary<int, TreeObject>();
// actual nested collection to return
var nested = new List<TreeObject>();

foreach (TreeObject item in list)
{
if (lookup.ContainsKey(item.ParentId))
{
// add to the parent's child list
lookup[item.ParentId].Children.Add(item);
}
else
{
// no parent added yet (or this is the first time)
nested.Add(item);
}
lookup.Add(item.Id, item);
}

return nested;
}

and a simple test (that works in LinqPad):

void Main()
{
var list = new List<TreeObject>() {
new TreeObject() { Id = 1, ParentId = 0, Name = "A" },
new TreeObject() { Id = 2, ParentId = 1, Name = "A.1" },
new TreeObject() { Id = 3, ParentId = 1, Name = "A.2" },
new TreeObject() { Id = 4, ParentId = 3, Name = "A.2.i" },
new TreeObject() { Id = 5, ParentId = 3, Name = "A.2.ii" }
};

FlatToHierarchy(list).Dump();
}

Results:

Sample Image

Since I'm updating this 5 years later, here's a recursive LINQ version:

public IList<TreeObject> FlatToHierarchy(IEnumerable<TreeObject> list, int parentId = 0) {
return (from i in list
where i.ParentId == parentId
select new TreeObject {
Id = i.Id,
ParentId = i.ParentId,
Name = i.Name,
Children = FlatToHierarchy(list, i.Id)
}).ToList();
}

How to group a flat un-categorized list into sub lists using XSLT grouping

Couldn't you do simply:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:template match="/html">
<body>
<xsl:for-each-group select="body/*" group-starting-with="p[@class='step']">
<xsl:choose>
<xsl:when test="self::p[@class='step']">
<step>
<xsl:copy-of select="."/>
<xsl:for-each-group select="current-group() except ." group-starting-with="p[@class='substep']">
<xsl:choose>
<xsl:when test="self::p[@class='substep']">
<substep>
<xsl:copy-of select="current-group()"/>
</substep>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</step>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</body>
</xsl:template>

</xsl:stylesheet>

Join siblings next to each other when two attribute conditions are met

As some XSLT 2 processors like Saxon 9 or 10 or Altova Raptor or XmlPrime also support XQuery 3 it might be more convenient to use the XQuery 3 window clause to formulate such conditions as it explicitly allows you to form end conditions where you have access to variable binding comparing the "last" item in a "window/group" to the following item (next):

<ROOT>
{
for tumbling window $w in ROOT/p
start when true()
end $e next $n when $e/@y != $n/@y or $n/@x - $e!(@width + @x) ge 4
return
<p>
{
head($w)/@*, $w/node()
}
</p>
}
</ROOT>

https://xqueryfiddle.liberty-development.net/3Nzd8bU

In XSLT 2/3 using for-each-group group-starting-with and assuming the grouping population is a sibling sequence you could use

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">

<xsl:mode on-no-match="shallow-copy"/>

<xsl:template match="ROOT">
<xsl:copy>
<xsl:for-each-group select="p"
group-starting-with="p[1] | p[preceding-sibling::p[1][@y != current()/@y or (@x - current()!(@width + @x) ge 4)]]">
<xsl:copy>
<xsl:apply-templates select="@*, current-group()/node()"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/naZYrpE/7

The used expression current()!(@with + @x) is XSLT/XPath 3 but you can use current()/(@with + @x) in XSLT 2. Instead of using the declarative xsl:mode to set up the identity transformation you would need to spell out the template <xsl:template match="@* | node()"><xsl:copy><xsl:apply-templates select="@* | node()"/></xsl:copy></xsl:template>.



Related Topics



Leave a reply



Submit