HiveBrain v1.2.0
Get Started
← Back to all entries
principlejavaMinor

Looping Strategy: Change behaviour after first iteration

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
afterloopingiterationfirstbehaviourstrategychange

Problem

I have often encountered the following situation. I want to call a method in a Loop, but want to skip the call at the first run. It do not have to be a method, it also can be some lines of code that I want to skip. Here is one example:

I have a file which looks like that.

headline:headline1
content:lorem ipsum dolor sit amet
content2:lorem ipsum dolor sit amet
content3:lorem ipsum dolor sit amet
headline:headline2
content1:lorem ipsum dolor sit amet
.
.
.


I iterate through the lines and generate following html:

 headline1 
   
     lorem ipsum dolor sit amet 
     lorem ipsum dolor sit amet 
     lorem ipsum dolor sit amet 
  
 headline2 
   
     lorem ipsum dolor sit amet 
  
  .
  .
  .


My Code looks like that:

private String createList(String file) {
        StringBuilder output = new StringBuilder();
        String[] lines = file.split("\n");

        int headlineCounter = 0;
        for (String line : lines) {                    
            String[] fields = line.split(":");    
            if (fields[0].equals("headline")) {
                if (headlineCounter > 0) {
                    output.append("");
                }
                output.append("").append(fields[1]).append("");
                output.append("");
                headlineCounter++;
            } else {
                output.append("").append(fields[1]).append("");    
            }
        }       
        output.append("");
        return output.toString();
    }


The content always is wrapped with an ` tag. When a new headline appears the content of the last headline is at the end and has to be closed with ` except it's the first headline of course, because there is no content before, that has to be closed.

In my example I am using a counter. On this way it works, but there are other and better ways I think. How would you solve this problem? This is just an example there are of course other and better ways to generate html, I just wanted to show you

Solution

One elegant way to handle this is to parse the input into a more usable structure before transforming it to HTML. For example, each section could be represented as:

private class Section {
  final String headline;
  final List contents = new ArrayList();

  Section(String headline) {
    this.headline = headline;
  }

  String asHtml() {
    StringBuilder sb = new StringBuilde();

    sb.append("").append(headline).append("");
    sb.append("");
    for (String item : contents) {
      sb.append("").append(item).append("");
    }
    sb.append("");

    return sb.toString();
  }
}


Note how the code clearly shows the structure of the resulting HTML.

The parsing code now has a more focused responsibility: parsing and validating the input. It has nothing to do with the output format any more.

private Iterable parseList(String fileContents) {
  List sections = new LinkedList();

  for (String line : fileContents.split("\n")) {
    String[] fields = line.split(":");
    if (fields.length != 2) {
      // throw some exception
    }

    if ("headline".equals(fields[0])) {
      sections.add(new Section(fields[1]));
      continue;
    }

    if (sections.isEmpty()) {
      // throw some error because "content" came before "headline"
    }

    sections.getLast().contents.add(fields[1]);
  }

  return sections;
}


I pointed out some lines with comments where you should do input validation. If you don't do this, you might get surprised by ArrayIndexOutOfBoundsExceptions if the input string is invalid.

Building an object that represents each section has the advantage that invalid HTML cannot be produces – your current code could emit a
  • outside of an `.



Then in your main code, the sections are simply concatenated together:

StringBuilder output = new StringBuilder();
for (Sections section : parseList(...)) {
  output.append(section.asHtml());
}


The advantage of separating the responsibilities of parsing and formatting is that it is now much easier to adapt a single part (e.g. to a new input or output format). My suggestion for the
Section class has to be criticized here, because it does not follow the Single Responsibility Principle: it both represents a section, and formats the output. Those should ideally be separated into two different classes, so that the formatting can vary without Section` having to change.

Code Snippets

private class Section {
  final String headline;
  final List<String> contents = new ArrayList<String>();

  Section(String headline) {
    this.headline = headline;
  }

  String asHtml() {
    StringBuilder sb = new StringBuilde();

    sb.append("<div>").append(headline).append("</div>");
    sb.append("<ul>");
    for (String item : contents) {
      sb.append("<li>").append(item).append("</li>");
    }
    sb.append("</ul>");

    return sb.toString();
  }
}
private Iterable<Section> parseList(String fileContents) {
  List<Section> sections = new LinkedList<Section>();

  for (String line : fileContents.split("\n")) {
    String[] fields = line.split(":");
    if (fields.length != 2) {
      // throw some exception
    }

    if ("headline".equals(fields[0])) {
      sections.add(new Section(fields[1]));
      continue;
    }

    if (sections.isEmpty()) {
      // throw some error because "content" came before "headline"
    }

    sections.getLast().contents.add(fields[1]);
  }

  return sections;
}
StringBuilder output = new StringBuilder();
for (Sections section : parseList(...)) {
  output.append(section.asHtml());
}

Context

StackExchange Code Review Q#37127, answer score: 7

Revisions (0)

No revisions yet.