Problem overview
The function clampAll gets an array of integers nums and two limits, low and high. It returns every number moved inside the limits, in the same order. A number below low becomes low, a number above high becomes high, and any other number stays as it is. The limits are never reversed: low is never greater than high.
The lesson splits the work between two functions. clamp is a helper: it gets one number and the two limits and returns that number moved inside them. clampAll is the function the tests call, and it calls the helper on every number. The lesson asks you to do the comparing yourself in the helper, without a built-in min, max or clamp function.
With nums = [12, -3, 7, 25], low = 0 and high = 20 the answer is [12, 0, 7, 20]: -3 is below 0, 25 is above 20, and 12 and 7 are inside.
Why a helper
Clamping one number is a small problem on its own. Writing it as a function lets you test it alone and keeps the loop easy to read: the loop only says "do this to every number", and the helper says what "this" is. Kotlin lets two functions in the same class call each other by name.
Step by step
- In
clamp, ifxis less thanlow, returnlow. - Below that, if
xis greater thanhigh, returnhigh. - After both checks, return
x. Only a number between the limits gets here, and it stays as it is. Noelseis needed, because eachreturnleaves the function. - In
clampAll, create an emptymutableListOf<Int>()namedclamped, in aval. The variable never changes to another list, but the list itself can grow. - Loop with
for (num in nums), one turn per number. - Inside, add the helper's answer:
clamped.add(clamp(num, low, high)). The loop needs noifof its own. - After the loop, return
clamped.
The solution in Kotlin
class Solution {
fun clamp(x: Int, low: Int, high: Int): Int {
if (x < low) {
return low
}
if (x > high) {
return high
}
return x
}
fun clampAll(nums: IntArray, low: Int, high: Int): List<Int> {
val clamped = mutableListOf<Int>()
for (num in nums) {
clamped.add(clamp(num, low, high))
}
return clamped
}
}
Two Kotlin details. The input is an IntArray, which you can loop over directly, while the answer is a List<Int> that you build up. And the helper's parameters are read-only: you return a value instead of changing x.
Why the order of the checks is safe
The two if statements do not interfere because low is never greater than high. A number cannot be both below low and above high. If it is below low, the helper returns low and never looks at the second check. If it is above high, the first check is false and the second one returns high. A number on a limit, such as 6 with both limits 6, fails both checks and comes back as it is, which is also the limit. When both limits are equal, every number ends up equal to that limit: clamp(5, 6, 6) and clamp(6, 6, 6) both return 6.
A built-in that does the same
Kotlin's standard library has coerceIn: 25.coerceIn(0, 20) is 20, (-3).coerceIn(0, 20) is 0 and 7.coerceIn(0, 20) is 7. The lesson asks you to write the comparing yourself so you see how a helper works, but it is good to know the library has it. Combined with map, it would even replace the loop: listOf(12, -3, 7, 25).map { it.coerceIn(0, 20) } gives [12, 0, 7, 20].
Testing
The helper alone, with limits 0 and 20: clamp(25, 0, 20) returns 20, clamp(-3, 0, 20) returns 0 and clamp(7, 0, 20) returns 7. The tests print lists as values separated by spaces. The three examples print:
12 0 7 20
-5 -1 4
6 6 6
The second uses negative limits, -5 and 5, so -8 becomes -5. In the third, both limits are 6, so every number becomes 6.
Complexity
The helper does a constant amount of work, and the loop calls it once per number. So the time grows in step with the length of nums. The answer is a new list of the same length, which is the only extra space. The input array is not changed: clampAll returns a new list instead of writing the clamped numbers back into nums.
Common mistakes
- Returning inside the loop. Only the first number would come back. Return after the loop.
- Swapping the limits in the call.
clamp(num, high, low)compiles but makes the helper treat the upper limit as the lower one. - Writing the loop's checks again. The helper already does the comparing; calling it keeps the loop to one line.
- Trying to assign to the parameter. Writing
x = lowinside the helper does not compile, because Kotlin parameters are read-only. Return the value instead, as the solution does. - Using
else ifbut forgetting the plain return. Every path through the helper must end in areturn, including the one for a number already inside the limits. - Forgetting the final
return x. Kotlin reports an error because the function must return anInton every path.
