Question

I'm working with symphony for a few weeks now and getting the hang of it, really loving all the base functionality.

I have a question since I'm not quite sure how to solve this: I have a news/blog page for the project I'm working on. Sometimes the articles are short sometimes very long. What I would like to do, is display an amount of paragraphs but stop when an x amount of characters are found. I Found methods to count the string length, but I would like it to work like this.

<p>paragraph with 20 chars</p>
<p>paragraph with 300 chars</p>
<p>paragraph with 500 chars</p> 

etc.

Now I would like to configure a max of lets say, 200 chars. the second paragraph will exceed the amount, but I want the content to be displayed both the first and second paragraph. so it will actually be 320 characters (excluding the html and whitespaces).

I hope I'm not to vague, and someone knows where I should go with this. The best option would be to edit the data source I guess, but it doesn't seem like a clean method to me allthough it should reduce the load slightly.

Anyone care to point me in the right direction?

Was it helpful?

Solution

If I read the question correctly, you want to output minimum paragraphs so that the total character count of the characters in the paragraphs is greater than the desired number of characters.

For example, if you have a paragraphs of 20, 30 and 40 characters. With a stop of 19, you'd only spit out the first, with a stop of 21 you'd output paragraphs 1 and 2 (21<20+30) and with any stop number greater than 50 you'd output all three.

If that is correct, the below output will do what you need - if you change this line:

<xsl:if test="30 &gt; string-length($prev)">

Where 30 becomes the desired length of string to stop at.

This XSLT template ...

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="ps">
        <h2>
            <xsl:value-of select="./@name"/>
        </h2>
        <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="p">
        <xsl:variable name="prev">
            <xsl:for-each select="preceding-sibling::node()">
                <xsl:value-of select="."/>
            </xsl:for-each>
        </xsl:variable>
        <xsl:if test="30 &gt; string-length($prev)">
            <xsl:copy-of select="."/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

... When applied to this document ...

<pss>
    <ps name="test1">
        <p>paragraph with 20 chars</p>
        <p>paragraph with 300 chars</p>
        <p>paragraph with 500 chars</p>
    </ps>
    <ps name="test2">
        <p>paragraph with 20 chars much greater characters</p>
        <p>paragraph with 300 chars</p>
        <p>paragraph with 500 chars</p>
    </ps>
    <ps name="test2">
        <p>few chars</p>
        <p>few chars</p>
        <p>few chars</p>
    </ps>
</pss>

... Produces this output.

<h2>test1</h2>
<div>
    <p>paragraph with 20 chars</p>
    <p>paragraph with 300 chars</p>
</div>
<h2>test2</h2>
<div>
    <p>paragraph with 20 chars much greater characters</p>
</div>
<h2>test2</h2>
<div>
    <p>few chars</p>
    <p>few chars</p>
    <p>few chars</p>
</div>

OTHER TIPS

For shit and giggles, I'm just adding what I had todo, to achieve what I wanted to achieve:

<xsl:for-each select="body/p">
            <xsl:variable name="prev">
            <xsl:for-each select="preceding-sibling::node()">
                <xsl:value-of select="."/>
            </xsl:for-each>
            </xsl:variable>
            <xsl:if test="50 &gt; string-length($prev)">
                <xsl:copy-of select="."/>
            </xsl:if>
</xsl:for-each>

It might ever help someone else looking for this so I guess I can add my own answer.

The solution from @stormtroopr will work, but it has O(n^2) performance: the time taken by your stylesheet will increase by a factor of 4 if the number of paragraphs doubles. A better solution is to use recursion. This is tricky until you get the hang of it, but it's not really complicated. Basically you process the first paragraph passing as a parameter the max length:

<xsl:apply-templates select="p[1]">
  <xsl:with-param name="length-so-far" select="0"/>
  <xsl:with-param name="max-length" select="200"/>
</xsl:apply-templates>

And then each paragraph first outputs itself, then makes a decision whether to continue with the next:

<xsl:template match="p">
  <xsl:param name="length-so-far"/>
  <xsl:param name="max-length"/>
  <xsl:if test="$length-so-far &lt; $max-length">
    <xsl:copy-of select="."/>
    <xsl:apply-templates select="following-sibling::p[1]">
      <xsl:with-param name="length-so-far" select="$length-so-far + string-length(.)"/>
      <xsl:with-param name="max-length" select="$max-length"/>
    </xsl:apply-templates>
  </xsl:if>
</xsl:template>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top