From 22fd7964989f07b60f78ac53a49e2cd1f68faa17 Mon Sep 17 00:00:00 2001 From: MrTob Date: Fri, 25 Oct 2024 10:49:26 +0200 Subject: [PATCH] ADD: new exercise --- .../result-analyzer/.docs/instructions.md | 70 +++++++++ .../result-analyzer/.docs/introduction.md | 8 ++ .../result-analyzer/.meta/Example.scala | 4 + .../result-analyzer/.meta/config.json | 25 ++++ exercises/practice/result-analyzer/build.sbt | 3 + .../result-analyzer/project/build.properties | 1 + .../src/main/scala/ResultsAnalysis.scala | 82 +++++++++++ .../src/test/scala/ResultsAnalysisTest.scala | 134 ++++++++++++++++++ 8 files changed, 327 insertions(+) create mode 100644 exercises/practice/result-analyzer/.docs/instructions.md create mode 100644 exercises/practice/result-analyzer/.docs/introduction.md create mode 100644 exercises/practice/result-analyzer/.meta/Example.scala create mode 100644 exercises/practice/result-analyzer/.meta/config.json create mode 100644 exercises/practice/result-analyzer/build.sbt create mode 100644 exercises/practice/result-analyzer/project/build.properties create mode 100644 exercises/practice/result-analyzer/src/main/scala/ResultsAnalysis.scala create mode 100644 exercises/practice/result-analyzer/src/test/scala/ResultsAnalysisTest.scala diff --git a/exercises/practice/result-analyzer/.docs/instructions.md b/exercises/practice/result-analyzer/.docs/instructions.md new file mode 100644 index 00000000..5c775e8e --- /dev/null +++ b/exercises/practice/result-analyzer/.docs/instructions.md @@ -0,0 +1,70 @@ +# Instructions + +The first line of the CSV file contains the headings, the other lines contain the results of the participants. +Each line has the following elements separated by commas: an id, a name, then 10 points for 10 exercises. +A maximum of 10 points can be achieved in each exercise. A value of -1 means that no solution has been +submitted. +The lines of the files can be read by: +```scala +val lines = readCSV() +``` +For the following calculations should be solved using Scala’s collections with their higher-order +functions. Try to implement each calculation by a single chain of operations. + +## Task 1: Building a list of Results objects +The first task is to build a list of Results objects from the read lines. Implement a case class Results +with an id of type Int, as name of type String and an list of points of type List[Int]. Then compute +the list of Results objects for the participants +```scala +val resultList : List[Results] = ??? +``` + +## Task 2: Number of solved tasks +From the list of Results objects, create a map that contains the number of solved exercises for each +student. An exercise is considered solved if at least 3 points have been achieved. + +## Task 3: Sufficient tasks solved +A student must solve at least 8 exercises to pass the course. Determine which students have submitted +enough solutions and which have not enough solutions. +```scala +val sufficientSolved : (Set[String], Set[String]) = ... +``` +_Hint: Use method partition._ + +## Task 4: Grading + +Next, the grades should be determined. The grades are calculated based on the points: There must be at least eight solutions with at least 3 +points. If this is the case, the grading is done based on the average of the best eight solutions according +to the following scheme: +``` +- [0.0 .. 5.0) : INSUFFICIENT +- [5.0 .. 6.25) : SUFFICIENT +- [6.25 .. 7.5) : SATISFACTORY +- [7.5 .. 8.75) : GOOD +- [8.75 .. 1.0] : EXCELLENT +``` +```scala +val grades: Map[String, Grade] = ... + ``` + +## Task 5: Grade statistics + +Compute statistic measurement from the grading. For each grade, compute the number of students with +that grade: +```scala +val nStudentsWithGrade : Map[Grade, Int] = ... +``` + +## Task 6: Number solved per assignment +For the 10 exercises, compute how many students have solved them (at least 3 points). Result is a list of +10 numbers, which indicate how many students have solved the exercises: +```scala +val nSolvedPerAssnmt : List[(Int, Int)] = ... +``` + +## Task 7: Average points per assignment +For the 10 exercises, compute the average of the achieved points. Only the submitted exercises (points +!= -1) should be considered: +```scala +val avrgPointsPerAssnmt : List[(Int, Double)] = ... +``` \ No newline at end of file diff --git a/exercises/practice/result-analyzer/.docs/introduction.md b/exercises/practice/result-analyzer/.docs/introduction.md new file mode 100644 index 00000000..8a4c32a2 --- /dev/null +++ b/exercises/practice/result-analyzer/.docs/introduction.md @@ -0,0 +1,8 @@ +# Processing Results with Collections +In this assignment, we want to use the Scala collections API for analyzing the results of a programming +course. The starting point is a CSV file in which the points achieved in 10 programming exercises are +stored for the course participants: +ID,Name,1,2,3,4,5,6,7,8,9,10 +1,Alred Maier,10,8,2,7,10,9,7,6,7,2 +2,Fritz Michalik,8,6,5,7,-1,-1,8,6,8,10 +... diff --git a/exercises/practice/result-analyzer/.meta/Example.scala b/exercises/practice/result-analyzer/.meta/Example.scala new file mode 100644 index 00000000..e9a49453 --- /dev/null +++ b/exercises/practice/result-analyzer/.meta/Example.scala @@ -0,0 +1,4 @@ +object ResultsAnalysis { + +} + diff --git a/exercises/practice/result-analyzer/.meta/config.json b/exercises/practice/result-analyzer/.meta/config.json new file mode 100644 index 00000000..4db0a2d6 --- /dev/null +++ b/exercises/practice/result-analyzer/.meta/config.json @@ -0,0 +1,25 @@ +{ + "authors": [ + "mrtob" + ], + "contributors": [ + "mrtob" + ], + "files": { + "solution": [ + "src/main/scala/ResultsAnalysis.scala" + ], + "test": [ + "src/test/scala/ResultsAnalysisTest.scala" + ], + "example": [ + ".meta/Example.scala" + ], + "invalidator": [ + "build.sbt" + ] + }, + "blurb": "Analyze the points of students in a class to ´genrate statistics.", + "source": "Exercism", + "source_url": "" +} diff --git a/exercises/practice/result-analyzer/build.sbt b/exercises/practice/result-analyzer/build.sbt new file mode 100644 index 00000000..803f8dc0 --- /dev/null +++ b/exercises/practice/result-analyzer/build.sbt @@ -0,0 +1,3 @@ +scalaVersion := "3.4.2" + +libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % Test diff --git a/exercises/practice/result-analyzer/project/build.properties b/exercises/practice/result-analyzer/project/build.properties new file mode 100644 index 00000000..ee4c672c --- /dev/null +++ b/exercises/practice/result-analyzer/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.10.1 diff --git a/exercises/practice/result-analyzer/src/main/scala/ResultsAnalysis.scala b/exercises/practice/result-analyzer/src/main/scala/ResultsAnalysis.scala new file mode 100644 index 00000000..b820e17c --- /dev/null +++ b/exercises/practice/result-analyzer/src/main/scala/ResultsAnalysis.scala @@ -0,0 +1,82 @@ +def readCSV(): List[String] = { + List( + "ID,Name,1,2,3,4,5,6,7,8,9,10", + "1,Jane Doe,9,10,9,6,10,9,10,10,10,9", + "2,John Doe,8,6,5,7,-1,-1,8,6,8,10", + "3,Jake Doe,10,9,9,-1,5,-1,8,-1,10,9", + "4,Jill Doe,10,8,2,7,10,9,7,6,7,2", + ) +} + +case class Results(id: Int, name: String, points: IndexedSeq[Int]) + +enum Grade: + case EXCELLENT, GOOD, SATISFACTORY, SUFFICIENT, INSUFFICIENT + +object ResultsAnalysis { + + def task1(lines: List[String]): List[Results] = + //TODO: Implement this method + ??? + + def task2(resultList: List[Results]): Map[String, Int] = + //TODO: Implement this method + ??? + + def task3(nSolvedPerStnd: Map[String, Int]): (Set[String], Set[String]) = + //TODO: Implement this method + ??? + + def task4(resultList: List[Results]): Map[String, Grade] = + //TODO: Implement this method + ??? + + def task5(resultList: List[Results]): Map[Grade, Int] = + //TODO: Implement this method + ??? + + def task6(resultList: List[Results]): List[(Int, Int)] = + //TODO: Implement this method + ??? + + def task7(resultList: List[Results]): List[(Int, Double)] = + //TODO: Implement this method + ??? + + def main(args: Array[String]): Unit = { + val lines = readCSV() + + // Task 1 + val resultList: List[Results] = task1(lines) + + // Task 2 + val nSolvedPerStnd = task2(resultList) + + // Task 3 + val sufficientSolved = task3(nSolvedPerStnd) + + // Task 4 + val grades = task4(resultList) + + //Task 5 + val nStudentsWithGrade = task5(resultList) + + //Task 6 + val nSolvedPerAssnmt = task6(resultList) + + //Task 7 + val avrgPointsPerAssnmt = task7(resultList) + } + + private def computeGrade(points: IndexedSeq[Int]): Grade = { + if (points.count(p => p >= 3) < 8) then Grade.INSUFFICIENT + else { + val avrg = points.sorted.drop(2).sum / 8 + if (avrg < 5.0) then Grade.INSUFFICIENT + else if (avrg < 6.5) then Grade.SUFFICIENT + else if (avrg < 8.0) then Grade.SATISFACTORY + else if (avrg < 9.0) then Grade.GOOD + else Grade.EXCELLENT + } + } +} \ No newline at end of file diff --git a/exercises/practice/result-analyzer/src/test/scala/ResultsAnalysisTest.scala b/exercises/practice/result-analyzer/src/test/scala/ResultsAnalysisTest.scala new file mode 100644 index 00000000..70568615 --- /dev/null +++ b/exercises/practice/result-analyzer/src/test/scala/ResultsAnalysisTest.scala @@ -0,0 +1,134 @@ +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.should.Matchers + +import scala.collection.immutable.HashMap + + +/** @version 1.2.0 */ +class ResultsAnalysisTest extends AnyFunSuite with Matchers { + + test("Task 1") { + val lines = List( + "ID,Name,1,2,3,4,5,6,7,8,9,10", + "1,Jane Doe,9,10,9,6,10,9,10,10,10,9", + "2,John Doe,8,6,5,7,-1,-1,8,6,8,10", + "3,Jake Doe,10,9,9,-1,5,-1,8,-1,10,9", + "4,Jill Doe,10,8,2,7,10,9,7,6,7,2", + ) + + val expectedResults = List( + Results(1, "Jane Doe", IndexedSeq(9, 10, 9, 6, 10, 9, 10, 10, 10, 9)), + Results(2, "John Doe", IndexedSeq(8, 6, 5, 7, -1, -1, 8, 6, 8, 10)), + Results(3, "Jake Doe", IndexedSeq(10, 9, 9, -1, 5, -1, 8, -1, 10, 9)), + Results(4, "Jill Doe", IndexedSeq(10, 8, 2, 7, 10, 9, 7, 6, 7, 2)) + ) + + val resultList = ResultsAnalysis.task1(lines) + resultList shouldEqual expectedResults + } + + test("Task 2") { + val results = List( + Results(1, "Jane Doe", IndexedSeq(9, 10, 9, 6, 10, 9, 10, 10, 10, 9)), + Results(2, "John Doe", IndexedSeq(8, 6, 5, 7, -1, -1, 8, 6, 8, 10)), + Results(3, "Jake Doe", IndexedSeq(10, 9, 9, -1, 5, -1, 8, -1, 10, 9)), + Results(4, "Jill Doe", IndexedSeq(10, 8, 2, 7, 10, 9, 7, 6, 7, 2)) + ) + + val expectedResults = Map( + "Jane Doe" -> 10, + "John Doe" -> 8, + "Jake Doe" -> 7, + "Jill Doe" -> 8 + ) + + val resultList = ResultsAnalysis.task2(results) + resultList shouldEqual expectedResults + } + + test("Task 3") { + val results = Map( + "Jane Doe" -> 10, + "John Doe" -> 8, + "Jake Doe" -> 7, + "Jill Doe" -> 8 + ) + + val expectedResults = (Set("Jane Doe", "John Doe", "Jill Doe"), Set("Jake Doe")) + + val resultList = ResultsAnalysis.task3(results) + resultList shouldEqual expectedResults + } + + test("Task 4") { + val results = List( + Results(1, "Jane Doe", IndexedSeq(9, 10, 9, 6, 10, 9, 10, 10, 10, 9)), + Results(2, "John Doe", IndexedSeq(8, 6, 5, 7, -1, -1, 8, 6, 8, 10)), + Results(3, "Jake Doe", IndexedSeq(10, 9, 9, -1, 5, -1, 8, -1, 10, 9)), + Results(4, "Jill Doe", IndexedSeq(10, 8, 2, 7, 10, 9, 7, 6, 7, 2)) + ) + + val expectedResults = Map( + "Jane Doe" -> Grade.EXCELLENT, + "John Doe" -> Grade.SATISFACTORY, + "Jake Doe" -> Grade.INSUFFICIENT, + "Jill Doe" -> Grade.GOOD + ) + + val resultList = ResultsAnalysis.task4(results) + resultList shouldEqual expectedResults + } + + test("Task 5") { + val results = List( + Results(1, "Jane Doe", IndexedSeq(9, 10, 9, 6, 10, 9, 10, 10, 10, 9)), + Results(2, "John Doe", IndexedSeq(8, 6, 5, 7, -1, -1, 8, 6, 8, 10)), + Results(3, "Jake Doe", IndexedSeq(10, 9, 9, -1, 5, -1, 8, -1, 10, 9)), + Results(4, "Jill Doe", IndexedSeq(10, 8, 2, 7, 10, 9, 7, 6, 7, 2)) + ) + + val expectedResults = HashMap( + Grade.EXCELLENT -> 1, + Grade.SATISFACTORY -> 1, + Grade.INSUFFICIENT -> 1, + Grade.GOOD -> 1 + ) + + val resultList = ResultsAnalysis.task5(results) + resultList shouldEqual expectedResults + } + + test("Task 6") { + val results = List( + Results(1, "Jane Doe", IndexedSeq(9, 10, 9, 6, 10, 9, 10, 10, 10, 9)), + Results(2, "John Doe", IndexedSeq(8, 6, 5, 7, -1, -1, 8, 6, 8, 10)), + Results(3, "Jake Doe", IndexedSeq(10, 9, 9, -1, 5, -1, 8, -1, 10, 9)), + Results(4, "Jill Doe", IndexedSeq(10, 8, 2, 7, 10, 9, 7, 6, 7, 2)) + ) + + val expectedResults = List( + (1, 4), (2, 4), (3, 3), (4, 3), (5, 3), (6, 2), (7, 4), (8, 3), (9, 4), (10, 3) + ) + + val resultList = ResultsAnalysis.task6(results) + resultList shouldEqual expectedResults + } + + test("Task 7"){ + val results = List( + Results(1, "Jane Doe", IndexedSeq(9, 10, 9, 6, 10, 9, 10, 10, 10, 9)), + Results(2, "John Doe", IndexedSeq(8, 6, 5, 7, -1, -1, 8, 6, 8, 10)), + Results(3, "Jake Doe", IndexedSeq(10, 9, 9, -1, 5, -1, 8, -1, 10, 9)), + Results(4, "Jill Doe", IndexedSeq(10, 8, 2, 7, 10, 9, 7, 6, 7, 2)) + ) + + val expectedResults = List( + (1,9.25), (2,8.25), (3,6.25), (4,6.666666666666667), (5,8.333333333333334), (6,9.0), (7,8.25), (8,7.333333333333333), (9,8.75), (10,7.5) + ) + + val resultList = ResultsAnalysis.task7(results) + resultList shouldEqual expectedResults + + } + +}