DEV Community

dev.to staff
dev.to staff

Posted on

Daily Challenge #121 - Who has the most money?

You're going on a trip with some students and it's up to you to keep track of how much money each Student has. A student is defined like this:

class Student:
def __init__(self, name, fives, tens, twenties):
self.name = name
self.fives = fives
self.tens = tens
self.twenties = twenties

As you can tell, each Student has some fives, tens, and twenties. Your job is to return the name of the student with the most money. If every student has the same amount, then return "all".

Notes:

  • Each student will have a unique name
  • There will always be a clear winner: either one person has the most, or everyone has the same amount
  • If there is only one student, then that student has the most money

This challenge comes from MrAppa on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Top comments (16)

Collapse
 
rafaacioly profile image
Rafael Acioly • Edited

there is nothing more beautiful than Python

from typing import List, Union from dataclasses import dataclass @dataclass class Student: name: str fives: int = 0 tens: int = 0 twenties: int = 0 def __str__(self): return self.name def __eq__(self, value): return self._amount() == value def __gt__(self, value): return self._amount() > value def __lt__(self, value): return self._amount() < value def __hash__(self): return self._amount() def _amount(self): return sum(( self.fives * 5, self.tens * 10, self.twenties * 20 )) def most_amount(students: List[Student]) -> str: uniques = set(students) return str(max(uniques)) if len(uniques) >= 2 else "all" 

Usage:

 student_1 = Student(name="richard", fives=25, tens=5, twenties=5) student_2 = Student(name="helen", fives=55, tens=5, twenties=5) print(most_amount([student_1, student_2])) # helen 
Collapse
 
hectorpascual profile image
Héctor Pascual

Hi, check this decorator :

docs.python.org/3/library/functool...

Will make you avoid writing some functions.

Collapse
 
rafaacioly profile image
Rafael Acioly

Thank you Héctor i didn't know about this.

I've tried to apply it but it looks is a little shady for me, i didn't quite understand what is does and how it "magically" does the comparison

Collapse
 
peterbunting profile image
Peter Bunting • Edited

My first attempt at F#

type Student(name,fives,tens,twenties) = member this.Name: string = name member this.Fives: int = fives member this.Tens: int = tens member this.Twenties: int = twenties member this.getStudentAmount with get () = (this.Fives * 5) + (this.Tens * 10) + (this.Twenties * 20) let a = new Student("a",1,1,1) let b = new Student("b",1,1,1) let c = new Student("c",1,1,1) let students = [|a;b;c|] |> Array.sortByDescending(fun x -> x.getStudentAmount) match students with |_ when students.Length = 1 -> students.[0].Name |_ when students.[0].getStudentAmount = students.[1].getStudentAmount -> "All" |_ -> students.[0].Name 

I couldn't get the syntax highlighting for F# so if anyone can point me in the right direction that would be much appreciated.

Collapse
 
jbristow profile image
Jon Bristow • Edited
 ```fsharp match students with |_ when students.Length = 1 -> students.[0].Name |_ when students.[0].getStudentAmount = students.[1].getStudentAmount -> "All" |_ -> students.[0].Name ``` 

renders as

 match students with |_ when students.Length = 1 -> students.[0].Name |_ when students.[0].getStudentAmount = students.[1].getStudentAmount -> "All" |_ -> students.[0].Name 

(HEY, dev.to maintainers, this is one of those "won't fix bugs!" 😠 Why can't I nest backticks as in the github flavored markdown spec?)

Collapse
 
peterbunting profile image
Peter Bunting

Thank you

Collapse
 
dak425 profile image
Donald Feury • Edited

I put myself in here as a test case as a joke because I didn't get an allowance as a kid 😂

education.go

package education type Trip struct { Students []Student } func (t Trip) Richest() string { sCount := len(t.Students) if sCount == 0 { return "none" } if sCount == 1 { return t.Students[0].Name } poorest, richest := t.Students[0], t.Students[0] for i := 1; i < sCount; i++ { if t.Students[i].Funds() < poorest.Funds() { poorest = t.Students[i] } else if t.Students[i].Funds() > richest.Funds() { richest = t.Students[i] } } if poorest.Funds() == richest.Funds() { return "all" } return richest.Name } type Student struct { Name string Fives int Tens int Twenties int } func (s Student) Funds() int { return (s.Fives * 5) + (s.Tens * 10) + (s.Twenties * 20) } 

education_test.go

package education import "testing" type baseTestCase struct { description string } var bob = Student{"Bob", 4, 0, 0} var mary = Student{"Mary", 0, 2, 0} var john = Student{"John", 0, 0, 1} var goat = Student{"Goat", 0, 0, 0} var richie = Student{"Richie", 10, 20, 100} func TestTrip_Richest(t *testing.T) { testCases := []struct { baseTestCase input Trip expected string }{ { baseTestCase{"no students"}, Trip{}, "none", }, { baseTestCase{"one student"}, Trip{[]Student{mary}}, mary.Name, }, { baseTestCase{"many students with same amount of funds"}, Trip{[]Student{mary, bob, john}}, "all", }, { baseTestCase{"many students with one having more"}, Trip{[]Student{mary, bob, goat, richie}}, richie.Name, }, } for _, test := range testCases { if result := test.input.Richest(); result != test.expected { t.Fatalf("FAIL: %s - %+v.Funds(): '%s' - expected: '%s'", test.baseTestCase.description, test.input, result, test.expected) } t.Logf("PASS: %s", test.baseTestCase.description) } } func TestStudent_Funds(t *testing.T) { testCases := []struct { baseTestCase input Student expected int }{ { baseTestCase{"no moniez"}, goat, 0, }, { baseTestCase{"only fives"}, bob, 20, }, { baseTestCase{"only tens"}, mary, 20, }, { baseTestCase{"only twenties"}, john, 20, }, { baseTestCase{"all the moniez"}, richie, 2250, }, } for _, test := range testCases { if result := test.input.Funds(); result != test.expected { t.Fatalf("FAIL: %s - %+v.Funds(): %d - expected: %d", test.baseTestCase.description, test.input, result, test.expected) } t.Logf("PASS: %s", test.baseTestCase.description) } } 
Collapse
 
seniru profile image
Seniru Pasan Indira

My try with python

import random as rand class Student: def __init__(self, name, fives, tens, twenties): self.name = name self.fives = fives self.tens = tens self.twenties = twenties def getMoney(self): return self.fives * 5 + self.tens * 10 + self.twenties def __str__(self): return self.name + " " + str(self.getMoney()) #Creating a list of students students = sorted([Student('Student' + str(s), rand.randint(0, 10), rand.randint(0, 10), rand.randint(0, 10)) for s in range(20) ], key=lambda s: s.getMoney(), reverse=True) for s in students: print(s) print('Student with highest amount:', end=' ') if len(students) == 1: print(students[1]) elif students[0] == students[::-1]: print('All') else: print(students[0]) 
Collapse
 
erezwanderman profile image
erezwanderman

Javascript:

class Student { constructor(name, fives, tens, twenties) { this.name = name; this.fives = fives; this.tens = tens; this.twenties = twenties; } } const mostMoney = (students) => { const studentsMoney = (student) => student.fives * 5 + student.tens * 10 + student.twenties * 20; if (students.length === 1) { return students[0].name; } if (students.length > 1) { const firstStudentMoney = studentsMoney(students[0]); const secondStudentMoney = studentsMoney(students[1]); if (firstStudentMoney === secondStudentMoney) { return 'all'; } let [maxName, maxMoney] = firstStudentMoney > secondStudentMoney ? [students[0].name, firstStudentMoney] : [students[1].name, secondStudentMoney]; for (let i = 2; i < students.length; i++) { const money = studentsMoney(students[i]); if (money > maxMoney) { [maxName, maxMoney] = [students[i].name, money]; } } return maxName; } } 

Usage:

const x = [ new Student('John1', 2, 0, 1), new Student('John2', 5, 1, 0), new Student('John3', 40, 1, 0), ]; appDiv.innerText = mostMoney(x); 
Collapse
 
peterbunting profile image
Peter Bunting

Thank you Michael. I have been following your F# posts in this series and found them very helpful.

I thought about using a record type but thought it would be easier to use a class property for the match function.

If you get chance please could you show how you would approach the full solution.

Many thanks.

Collapse
 
jbristow profile image
Jon Bristow • Edited

Ridiculously over-engineered Kotlin version:

data class Student(val name: String, val fives: Int, val tens: Int, val twenties: Int) private val Student.total: Int get() = fives * 5 + tens * 10 + twenties * 20 sealed class StudentAccumulator { abstract val money: Int } object Empty : StudentAccumulator() { override val money = 0 } data class ErrorFound(val message: String) : StudentAccumulator() { override val money = 0 } data class MaxFound(val name: String, override val money: Int) : StudentAccumulator() data class TieFound(override val money: Int) : StudentAccumulator() fun generateStudentFolder(compareFn: (StudentAccumulator, Student) -> Int) = { acc: StudentAccumulator, student: Student -> when (acc) { is ErrorFound -> acc is Empty -> MaxFound(student.name, student.total) is MaxFound -> acc.consider(student, compareFn) is TieFound -> acc.consider(student, compareFn) } } fun MaxFound.consider(student: Student, compareFn: (StudentAccumulator, Student) -> Int) = when (compareFn(this, student)) { -1 -> MaxFound(student.name, student.total) 0 -> TieFound(student.total) else -> this } fun TieFound.consider(student: Student, compareFn: (StudentAccumulator, Student) -> Int) = when (compareFn(this, student)) { -1 -> MaxFound(student.name, student.total) 0 -> ErrorFound("More than one maximum") else -> this } fun Iterable<Student>.findMax() = fold( Empty as StudentAccumulator, generateStudentFolder { a, b -> a.money.compareTo(b.total) } ) fun Iterable<Student>.findMaxName(): String { return when (val result = findMax()) { is ErrorFound -> throw Error(result.message) is Empty -> throw Error("No max found") is MaxFound -> result.name is TieFound -> "all" } } fun main() { val studentsA = listOf(Student("a", 1, 1, 1)) println(studentsA.findMaxName()) val studentsB = listOf( Student("a", 1, 1, 1), Student("b", 1, 1, 1) ) println(studentsB.findMaxName()) val studentsC = listOf( Student("a", 1, 1, 1), Student("c", 2, 1, 1), Student("b", 1, 1, 1) ) println(studentsC.findMaxName()) } 

It's typesafe, and we could change the comparator out if we wanted to. A little kludgey in the fold section (I should probably involve Option to avoid people accidentally calling money on the Empty and Error types. 🤷‍♀️

While I love the sealed class idea in Kotlin, type erasure and lack of true pattern matching really hampers the readability. If I was able to rely on StudentAccumulator auto-casting to its internal types, then I could remove a when statement that just dispatches out to the same overloaded call with the proper type.

Collapse
 
jbristow profile image
Jon Bristow

Ok, I fixed it. It's not too much uglier with proper Option protection...

import arrow.core.Option import arrow.core.getOrElse data class Student(val name: String, val fives: Int, val tens: Int, val twenties: Int) private val Student.total: Int get() = fives * 5 + tens * 10 + twenties * 20 sealed class StudentAccumulator { abstract val money: Option<Int> } object Empty : StudentAccumulator() { override val money = Option.empty<Int>() } data class ErrorFound(val message: String) : StudentAccumulator() { override val money = Option.empty<Int>() } data class MaxFound(val name: String, override val money: Option<Int>) : StudentAccumulator() { constructor(student: Student) : this(student.name, Option.just(student.total)) } data class TieFound(override val money: Option<Int>) : StudentAccumulator() { constructor(student: Student) : this(Option.just(student.total)) } fun generateStudentFolder(compareFn: (StudentAccumulator, Student) -> Option<Int>) = { acc: StudentAccumulator, student: Student -> when (acc) { is ErrorFound -> acc is Empty -> MaxFound(student) is MaxFound -> acc.consider(student, compareFn) is TieFound -> acc.consider(student, compareFn) } } fun MaxFound.consider(student: Student, compareFn: (StudentAccumulator, Student) -> Option<Int>) = compareFn(this, student).map { result -> when (result) { -1 -> MaxFound(student) 0 -> TieFound(student) else -> this } }.getOrElse { ErrorFound("Bad comparison.") } fun TieFound.consider(student: Student, compareFn: (StudentAccumulator, Student) -> Option<Int>) = compareFn(this, student).map { result -> when (result) { -1 -> MaxFound(student) 0 -> ErrorFound("More than one maximum") else -> this } }.getOrElse { ErrorFound("Bad comparison.") } fun Iterable<Student>.findMax() = fold( Empty as StudentAccumulator, generateStudentFolder { a, b -> a.money.map { it.compareTo(b.total) } } ) fun Iterable<Student>.findMaxName(): String { return when (val result = findMax()) { is ErrorFound -> throw Error(result.message) is Empty -> throw Error("No max found") is MaxFound -> result.name is TieFound -> "all" } } 
Collapse
 
logycon profile image
Igor

In Scala -

case class Student(name : String, fives: Int, tens: Int, twenties: Int) { override def toString = s"${name} -> ${worth}" def worth: Int = 5 * fives + 10 * tens + 20 * twenties } val students = List( Student("1", 5, 1, 4), Student("2", 5, 1, 4), Student("3", 5, 1, 4) ) val winner = students.maxBy(f => f.worth) val winners = (winner :: students.filter(f => f.worth == winner.worth)).distinct if (winners.size == students.size) println("all") else println(winner) 
Collapse
 
peter279k profile image
peter279k

Here is the Python code snippets:

def most_money(students): student_name = '' sum_student = 0 check_all = 0 for student in students: current_sum = student.fives * 5 + student.tens * 10 + student.twenties * 20 if current_sum > sum_student: sum_student = current_sum student_name = student.name elif current_sum == sum_student: if check_all == 0: check_all += 2 else: check_all += 1 if check_all == len(students): return 'all' return student_name 
Collapse
 
peterbunting profile image
Peter Bunting

Wow. Very good. I was nowhere near getting this. I need to spend some time working through your solution.

Thank you - it is very instructional. I really like how you show your tests as well. I haven't quite got my head around setting up tests in F# yet.