Dart: An Algorithm to Unindent Code
Have you ever had to write an algorithm to unindent a block of code consistently? Figuring out the maximum amount of whitespace among all the lines and then removing that amount of whitespace is non-trivial. Remembering to ignore lines that only have whitespace makes the problem even harder. Here's some Dart code that I wrote to do it. I do believe the performance is O(num_lines * max_line_length), which I think is optimal. It should be easy to port this to other languages if you need it:
/**
* Remove the indentation from the given lines.
*
* Only remove as much indentation as the line with the least amount of
* indentation.
*/
List<String> unindentFilter(List<String> lines) {
// Make a copy so that we can modify it in place.
lines = new List<String>.from(lines);
// Make sure there is at least one line.
if (!lines.isEmpty()) {
// Figure out how much indentation the first line has.
var indentation = new List<String>();
for (String char in lines[0].splitChars()) {
if (char == " " || char == "\t") {
indentation.add(char);
} else {
break;
}
}
// Figure out the least amount of indentation any of the other lines has.
for (var i = 1; i < lines.length; i++) {
String line = lines[i];
List<String> chars = line.splitChars();
// Lines that only have whitespace should be set to "" and ignored.
var whitespaceOnly = true;
for (var char in chars) {
if (char != " " && char != "\t") {
whitespaceOnly = false;
break;
}
}
if (whitespaceOnly) {
lines[i] = "";
} else {
// If the line has something other than whitespace, see how its
// indentation compares to the least amount of indentation we've
// seen so far.
for (var j = 0; j < indentation.length && j < chars.length; j++) {
String char = chars[j];
if ((char != " " && char != "\t") ||
char != indentation[j]) {
// We found a new line with less indentation.
indentation.removeRange(j, indentation.length - j);
break;
}
}
}
}
// Loop over all the lines, and remove the right amount of indentation.
for (var i = 0; i < lines.length; i++) {
String line = lines[i];
// Ignore blank lines.
if (line != "") {
// Otherwise, trim off the right amount of indentation.
List<String> chars = line.splitChars();
List<String> unindented = chars.getRange(indentation.length, chars.length - indentation.length);
lines[i] = Strings.concatAll(unindented);
}
}
}
return lines;
}Here are some tests:
test("unindentFilter unindents code", () {
expect(merger.unindentFilter([" 1",
" 2"]),
equals(["1",
"2"]));
});
test("unindentFilter unindents code where the first line is indented the most", () {
expect(merger.unindentFilter(["\t 1",
"\t 2",
"\t 3"]),
equals([" 1",
"2",
" 3"]));
});
test("unindentFilter does nothing for unindented code", () {
expect(merger.unindentFilter(["1",
"2",
"3"]),
equals(["1",
"2",
"3"]));
});
test("unindentFilter handles empty lists", () {
expect(merger.unindentFilter([]),
equals([]));
});
test("unindentFilter does not try to handle inconsistent indentation", () {
expect(merger.unindentFilter(["\t1",
" 2",
" 3"
" 4"]),
equals(["\t1",
" 2",
" 3"
" 4"]));
});
test("unindentFilter handles really awkward short lines", () {
expect(merger.unindentFilter([" 1",
"2"]),
equals([" 1",
"2"]));
});
test("unindentFilter handles blank lines and lines with only indentation", () {
expect(merger.unindentFilter([" 1",
"",
" ",
" 2"]),
equals(["1",
"",
"",
" 2"]));
});
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





