principlejavaMinor
Looping Strategy: Change behaviour after first iteration
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.
I iterate through the lines and generate following html:
My Code looks like that:
The content always is wrapped with an `
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
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:
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.
I pointed out some lines with comments where you should do input validation. If you don't do this, you might get surprised by
Building an object that represents each section has the advantage that invalid HTML cannot be produces – your current code could emit a
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.