In your code, you found a complex collection processing implemented using classic Java-like techniques. Your task is to refactor this code to use Kotlin collection processing functions. The function is responsible for finding the best students for internships. This is what is looks like:
fun List<StudentGrades>.getBestForScholarship(
semester: String
): List<StudentGrades> {
val students = this
var candidates = mutableListOf<StudentGrades>()
for (s in students) {
var ectsPointsGained = 0
for (g in s.grades) {
if (g.semester == semester && g.passing) {
ectsPointsGained += g.ects
}
}
if (ectsPointsGained > 30) {
candidates.add(s)
}
}
Collections.sort(candidates, { s1, s2 ->
val difference =
averageGradeFromSemester(s2, semester) -
averageGradeFromSemester(s1, semester)
if (difference > 0) 1 else -1
})
val best = mutableListOf<StudentGrades>()
for (i in 0 until 10) {
val next = candidates.getOrNull(i)
if (next != null) {
best.add(next)
}
}
return best
}
private fun averageGradeFromSemester(
student: StudentGrades,
semester: String
): Double {
var sum = 0.0
var count = 0
for (g in student.grades) {
if (g.semester == semester) {
sum += g.grade
count++
}
}
return sum / count
}
data class Grade(
val passing: Boolean,
var ects: Int,
var semester: String,
var grade: Double
)
data class StudentGrades(
val studentId: String,
val grades: List<Grade>
)
Without changing behavior, refactor those functions.
This problem can either be solved in the below playground or you can clone kotlin-exercises project and solve it locally. In the project, you can find code template for this exercise in functional/collections/StudentGradesIntenship.kt. You can find there starting code and unit tests.
Hint: To calculate an average of all the numbers in a list, you can use the average function.
Once you are done with the exercise, you can check your solution here.
Playground
import org.junit.Test
import java.util.Collections
import kotlin.random.Random
import kotlin.test.assertEquals
fun List<StudentGrades>.getBestForScholarship(
semester: String
): List<StudentGrades> {
val students = this
var candidates = mutableListOf<StudentGrades>()
for (s in students) {
var ectsPointsGained = 0
for (g in s.grades) {
if (g.semester == semester && g.passing) {
ectsPointsGained += g.ects
}
}
if (ectsPointsGained > 30) {
candidates.add(s)
}
}
Collections.sort(candidates, { s1, s2 ->
val difference =
averageGradeFromSemester(s2, semester) -
averageGradeFromSemester(s1, semester)
if (difference > 0) 1 else -1
})
val best = mutableListOf<StudentGrades>()
for (i in 0 until 10) {
val next = candidates.getOrNull(i)
if (next != null) {
best.add(next)
}
}
return best
}
private fun averageGradeFromSemester(
student: StudentGrades,
semester: String
): Double {
var sum = 0.0
var count = 0
for (g in student.grades) {
if (g.semester == semester) {
sum += g.grade
count++
}
}
return sum / count
}
data class Grade(
val passing: Boolean,
var ects: Int,
var semester: String,
var grade: Double
)
data class StudentGrades(
val studentId: String,
val grades: List<Grade>
)
class StudentGradesIntenshipTest {
@Test
fun `should return best students for scholarship`() {
val r = Random(123456789)
val grades = List(100) {
StudentGrades(
"S$it",
List(100) {
Grade(
passing = r.nextBoolean(),
ects = r.nextInt(5) + 1,
semester = "Semester ${r.nextInt(5)}",
grade = r.nextDouble() * 5 + 2.0
)
}
)
}
val res1 = grades.getBestForScholarship("Semester 0")
assertEquals(
listOf("S83", "S47", "S26", "S53", "S4", "S29", "S50", "S34", "S44", "S59"),
res1.map { it.studentId },
)
assertEquals(
listOf("S30", "S66", "S49", "S14", "S6", "S12", "S25", "S99", "S7", "S40"),
grades.getBestForScholarship("Semester 1").map { it.studentId }
)
}
@Test
fun `should return empty list if no students passed`() {
val grades = listOf(
StudentGrades(
"S1",
listOf(
Grade(false, 5, "Semester 0", 3.0),
Grade(false, 5, "Semester 1", 3.0),
Grade(false, 5, "Semester 2", 3.0),
Grade(false, 5, "Semester 3", 3.0),
Grade(false, 5, "Semester 4", 3.0),
)
),
StudentGrades(
"S2",
listOf(
Grade(false, 5, "Semester 0", 3.0),
Grade(false, 5, "Semester 1", 3.0),
Grade(false, 5, "Semester 2", 3.0),
Grade(false, 5, "Semester 3", 3.0),
Grade(false, 5, "Semester 4", 3.0),
)
),
StudentGrades(
"S3",
listOf(
Grade(false, 5, "Semester 0", 3.0),
Grade(false, 5, "Semester 1", 3.0),
Grade(false, 5, "Semester 2", 3.0),
Grade(false, 5, "Semester 3", 3.0),
Grade(false, 5, "Semester 4", 3.0),
)
),
)
assertEquals(
emptyList<String>(),
grades.getBestForScholarship("Semester 0").map { it.studentId }
)
}
@Test
fun `should sort by average grade for semester`() {
val grades = listOf(
StudentGrades(
"S1",
listOf(
Grade(true, 25, "Semester 1", 3.0),
Grade(true, 25, "Semester 1", 3.0),
)
),
StudentGrades(
"S2",
listOf(
Grade(true, 50, "Semester 1", 4.0),
Grade(true, 50, "Semester 2", 1.0),
Grade(true, 50, "Semester 3", 1.0),
Grade(true, 50, "Semester 4", 1.0),
)
),
StudentGrades("S3", listOf(Grade(true, 50, "Semester 1", 5.0),)),
)
assertEquals(
listOf("S3", "S2", "S1"),
grades.getBestForScholarship("Semester 1").map { it.studentId }
)
}
@Test
fun `should return 10 best students`() {
val grades = List(100) {
StudentGrades(
"S$it",
listOf(
Grade(
passing = true,
ects = 50,
semester = "Semester 0",
grade = 5.0 + (0.01 * it)
)
)
)
}
assertEquals(
List(10) { "S${99 - it}" },
grades.getBestForScholarship("Semester 0").map { it.studentId }
)
}
@Test
fun `should calculate average grade from semester`() {
assertEquals(
3.0,
averageGradeFromSemester(StudentGrades(
"S1",
listOf(
Grade(true, 25, "Semester 1", 3.0),
Grade(true, 25, "Semester 1", 3.0),
)
), "Semester 1")
)
assertEquals(
3.0,
averageGradeFromSemester(StudentGrades(
"S1",
listOf(
Grade(true, 10, "Semester 1", 2.0),
Grade(true, 20, "Semester 1", 4.0),
)
), "Semester 1")
)
assertEquals(
2.0,
averageGradeFromSemester(StudentGrades(
"S1",
listOf(
Grade(true, 10, "Semester 1", 2.0),
Grade(true, 20, "Semester 2", 4.0),
)
), "Semester 1")
)
}
}
Marcin Moskala is a highly experienced developer and Kotlin instructor as the founder of Kt. Academy, an official JetBrains partner specializing in Kotlin training, Google Developers Expert, known for his significant contributions to the Kotlin community. Moskala is the author of several widely recognized books, including "Effective Kotlin," "Kotlin Coroutines," "Functional Kotlin," "Advanced Kotlin," "Kotlin Essentials," and "Android Development with Kotlin."
Beyond his literary achievements, Moskala is the author of the largest Medium publication dedicated to Kotlin. As a respected speaker, he has been invited to share his insights at numerous programming conferences, including events such as Droidcon and the prestigious Kotlin Conf, the premier conference dedicated to the Kotlin programming language.