...the blocks within if statements, else statements, while statements, and so on should be one line long. Probably that line should be a function call. Not only does this keep the enclosing function small, but it also adds documentary value because the function called within the block can have a nicely descriptive name. This also implies that functions should not be large enough to hold nested structures. Therefore, the indent level of a function should not be greater than one or two. This, of course, makes the functions easier to read and understand.
Do whatever, jump over your head, but avoid indentation of 5 tabs! If I see 5 tabs in your code, you are no more in the project.
if <condition 1> then
[code fragment 1]
else
if <condition 2> then
[code fragment 2]
else
if <condition 3> then
[code fragment 3]
else
[code fragment 4]
end if
end if
end if
if <condition 1> then
[code fragment 1]
elseif <condition 2> then
[code fragment 2]
elseif <condition 3> then
[code fragment 3]
else
[code fragment 4]
end if
while ([loop condition]) {
if ([condition 1]) {
if ([condition 2]) {
if [condition 3]) {
[code fragment with its own indenting levels]
}
}
}
}
while ([loop condition]) {
if (![condition 1]) continue;
if (![condition 2]) continue;
if (![condition 3]) continue;
[code fragment with its own indenting levels]
}
private fun retrieveStats(rowsLimit: String): Stats { // ##################### 1st tab is not counted - it indents functions inside class
val sqlSelect = "SELECT " + // ##################### 2nd tab is not counted too - it indents code inside function; so, this expression has the indentation of zero!!!
// ##################### All subsequent tabs are not counted too - they are inside an expression; their goal is to make code more readable:
"ROUND(AVG((STRFTIME('%s', ${DbColumn.BETWEEN_MEALS_START}) - STRFTIME('%s', ${DbColumn.MEAL_1_START})) / 60)) AS ${DbColumn.AVG_MEAL_1}, " +
"ROUND(AVG((STRFTIME('%s', ${DbColumn.MEAL_2_START}) - STRFTIME('%s', ${DbColumn.BETWEEN_MEALS_START})) / 60)) AS ${DbColumn.AVG_BETWEEN_MEALS}, " +
"ROUND(AVG((STRFTIME('%s', ${DbColumn.FASTING_START}) - STRFTIME('%s', ${DbColumn.MEAL_2_START})) / 60)) AS ${DbColumn.AVG_MEAL_2}, " +
"ROUND(AVG((STRFTIME('%s', ${DbColumn.FASTING_START}) - STRFTIME('%s', ${DbColumn.MEAL_1_START})) / 60)) AS ${DbColumn.AVG_EW}, " +
"COUNT(1) AS ${DbColumn.MEAL_1_COUNT}, " + // can also be used as total cycles count
"(" +
"SELECT COUNT(1) " +
"FROM ${DbTable.CYCLE} " +
"WHERE ${DbColumn.ID} IN (SELECT ${DbColumn.ID} " + // limit by the same condition as the overall query...
"FROM ${DbTable.CYCLE} " +
"WHERE ${DbColumn.FASTING_START} IS NOT NULL " +
"ORDER BY ${DbColumn.ID} DESC " +
"LIMIT $rowsLimit) " +
"AND ${DbColumn.ID} IN (SELECT ${DbColumn.ID} " + // ...and exclude OMADs from that population
"FROM ${DbTable.CYCLE} " +
"WHERE ${DbColumn.MEAL_2_START} IS NOT NULL)" +
") AS ${DbColumn.MEAL_2_COUNT} " +
"FROM ${DbTable.CYCLE} " +
"WHERE ${DbColumn.ID} IN (SELECT ${DbColumn.ID} " +
"FROM ${DbTable.CYCLE} " +
"WHERE ${DbColumn.FASTING_START} IS NOT NULL " +
"ORDER BY ${DbColumn.ID} DESC " +
"LIMIT $rowsLimit)"
val s = crudHelper.retrieveOne<Stats>(sqlSelect, required = true)!!
s.avgMeal = (
(s.avgMeal1!! * s.meal1Count!! + s.avgMeal2!! * s.meal2Count!!).toDouble()
/
(s.meal1Count!! + s.meal2Count!!).toDouble()
).roundToInt()
s.omadsCount = s.meal1Count!! - s.meal2Count!!
s.omadsPct = (s.omadsCount!!.toDouble() / s.meal1Count!!.toDouble() * 100).roundToInt()
return s
}