Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
9446c9d3c1 |
@ -1,203 +1,80 @@
|
|||||||
package TopSongs
|
package TopSongs
|
||||||
|
import scala.io.Source
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import com.github.tototoshi.csv._
|
import com.github.tototoshi.csv._
|
||||||
|
|
||||||
trait Person {
|
//jpc: seems all ok but can you add more interesting things to make the class structure more interesting?
|
||||||
|
//jpc perhaps adding enum for the Genre or type of band or person, or the sub-genre: hard rock, etc. If you don't have enough data you can add some data yourself just for fun
|
||||||
|
trait Person:
|
||||||
val name: String
|
val name: String
|
||||||
}
|
|
||||||
|
|
||||||
case class Singer(name: String) extends Person
|
|
||||||
|
|
||||||
|
case class Artist(name: String) extends Person
|
||||||
case class Writer(name: String) extends Person
|
case class Writer(name: String) extends Person
|
||||||
|
|
||||||
case class Producer(name: String) extends Person
|
case class Producer(name: String) extends Person
|
||||||
|
class Album(title: String, label: String)
|
||||||
// The parametrized type Artist can be a Singer, a Writer or a Producer
|
class Streak(val s: String) :
|
||||||
// They do a different creation action
|
val streak: Option[Int] =
|
||||||
class Artist[A <: Person](val person: A) {
|
|
||||||
def create(): String = {
|
|
||||||
person match {
|
|
||||||
case s: Singer => s"${s.name} sing"
|
|
||||||
case w: Writer => s"${w.name} write"
|
|
||||||
case p: Producer => s"${p.name} produce"
|
|
||||||
case _ => s"${person.name} create"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Defined a MusicGender as en enum
|
|
||||||
enum MusicGenre:
|
|
||||||
case Rock, Soul, Blues, Folk, Funk, Reggae, HipHop, NewWave, Electro, Metal
|
|
||||||
|
|
||||||
// Define an Album with a union between a String and a MusicGenre
|
|
||||||
class Album(title: String, label: String, genre: String|MusicGenre) {
|
|
||||||
def getGenre(): String | MusicGenre = {
|
|
||||||
genre
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Streak(val s: String) {
|
|
||||||
val streak: Option[Int] = {
|
|
||||||
val regex = "\\d+".r
|
val regex = "\\d+".r
|
||||||
if(s.equalsIgnoreCase("Did not chart")) {
|
if(s.equalsIgnoreCase("Did not chart")) then Some(0)
|
||||||
Some(0)
|
else regex.findFirstIn(s) match
|
||||||
} else {
|
|
||||||
regex.findFirstIn(s) match {
|
|
||||||
case Some(value) => Some(Integer.parseInt(value))
|
|
||||||
case None => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Position(val p: String) {
|
|
||||||
val pos: Option[Int] = {
|
|
||||||
val regex = "\\d+".r
|
|
||||||
regex.findFirstIn(p) match {
|
|
||||||
case Some(value) => Some(Integer.parseInt(value))
|
case Some(value) => Some(Integer.parseInt(value))
|
||||||
case None => None
|
case None => None
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defined a type alias Rank which is a tuple of Streak and Position
|
class Position(val p: String) :
|
||||||
// Streak and Position are linked together, it's why there are regrouped in the Rank type alias
|
val position: Option[Int] =
|
||||||
type Rank = (Streak, Position)
|
val regex = "\\d+".r
|
||||||
|
regex.findFirstIn(p) match
|
||||||
// Defined an intersection type of Artist & Writer & Producer
|
case Some(value) => Some(Integer.parseInt(value))
|
||||||
// Defined with an alias : God
|
case None => None
|
||||||
// It's a way to define a type that is a combination of other types
|
|
||||||
// If an Artist is also a Writer and a Producer, it's basically a God of the music
|
|
||||||
type God = Singer & Writer & Producer
|
|
||||||
|
|
||||||
class Song(
|
class Song(
|
||||||
val title: String,
|
val title: String,
|
||||||
val description: Option[String],
|
val description: Option[String],
|
||||||
val singer: List[Artist[Singer]],
|
val artist: List[Artist],
|
||||||
val writer: List[Artist[Writer]],
|
val writer: List[Writer],
|
||||||
val producer: List[Artist[Producer]],
|
val producer: List[Producer],
|
||||||
val album: Option[Album],
|
val album: Option[Album],
|
||||||
val rank: Rank
|
val streak: Streak,
|
||||||
|
val position: Position
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object TopSongs :
|
||||||
|
var songs: List[Song] = List() //jpc: consider not using mutable variables
|
||||||
|
def addSong(song: Song): Unit =
|
||||||
|
songs = song :: songs
|
||||||
|
|
||||||
|
def printSongs(): Unit =
|
||||||
|
songs.foreach(song => println(
|
||||||
|
s"${song.title} by ${song.artist.map(_.name).mkString(", ")} " +
|
||||||
|
s"produced by ${song.producer.map(_.name).mkString(", ")} " +
|
||||||
|
s"spent ${song.streak.streak.getOrElse("no")} weeks " +
|
||||||
|
s"on the charts on Pos. ${song.position.position.getOrElse("NA")}"
|
||||||
|
))
|
||||||
|
|
||||||
case class TopSongs(songs: List[Song] = List()) {
|
|
||||||
def addSong(song: Song): TopSongs = {
|
|
||||||
TopSongs(song :: songs)
|
|
||||||
}
|
|
||||||
def printSongs(): Unit = {
|
|
||||||
songs.map(song => {
|
|
||||||
val title = song.title
|
|
||||||
val singer = song.singer
|
|
||||||
val producers = song.producer.map(_.create()).mkString(", ")
|
|
||||||
val streak = song.rank._1.streak.getOrElse("no")
|
|
||||||
val pos = song.rank._2.pos.getOrElse("NA")
|
|
||||||
|
|
||||||
singer match {
|
|
||||||
case s if s.exists(_.person.isInstanceOf[God]) => println(s"$title by God ${singer.map(_.person.name).mkString(", ")} spent $streak weeks on the charts on Pos. $pos")
|
|
||||||
case _ => println(
|
|
||||||
s"$title by ${singer.map(_.person.name).mkString(", ")}. " +
|
|
||||||
s"$producers this song that " +
|
|
||||||
s"spent $streak weeks " +
|
|
||||||
s"on the charts on Pos. $pos"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function filter the songs by a specific music gender
|
|
||||||
def filterByGenre(genre: MusicGenre): List[Song] = {
|
|
||||||
songs.filter(song => song.album.exists(_.getGenre() == genre))
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function counts the number of songs by each artist
|
|
||||||
// It uses flatMap to extract the artist names from the songs
|
|
||||||
// Then it groups the songs by artist name and counts the occurrences
|
|
||||||
// Finally, it returns a map with the artist names as keys and the counts as values
|
|
||||||
def countSongsByArtist(): Map[String, Int] = {
|
|
||||||
songs.flatMap(_.singer.map(_.person.name))
|
|
||||||
.groupBy(identity)
|
|
||||||
.view.mapValues(_.size)
|
|
||||||
.toMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function return the song spent the most weeks on the chart
|
|
||||||
// It uses reduce to find the song with the maximum streak value
|
|
||||||
def longestStreak(): Song = {
|
|
||||||
songs.reduce((s1, s2) => {
|
|
||||||
if (s1.rank._1.streak.getOrElse(0) > s2.rank._1.streak.getOrElse(0)) s1 else s2
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function returns the top N songs by weeks on chart
|
|
||||||
// It sorts the songs by the streak value in descending order and takes the top N songs
|
|
||||||
def topNSongsByWeeks(n: Int): List[Song] = {
|
|
||||||
songs.sortBy(song => -song.rank._1.streak.getOrElse(0)).take(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@main def main(): Unit =
|
@main def main(): Unit =
|
||||||
// create a new TopSongs object
|
// create a new TopSongs object
|
||||||
var topSongs = TopSongs()
|
val topSongs = TopSongs
|
||||||
|
|
||||||
val reader = CSVReader.open(new File("src/main/resources/songs.csv"))
|
val reader = CSVReader.open(new File("src/main/resources/songs.csv"))
|
||||||
val allRows = reader.allWithHeaders()
|
val allRows = reader.allWithHeaders()
|
||||||
for (row <- allRows) {
|
for (row <- allRows) do
|
||||||
val title = row("title")
|
|
||||||
val description = row("description")
|
|
||||||
val singer = List(Artist[Singer](Singer(row("artist"))))
|
|
||||||
val writers = List(Artist[Writer](Writer(row("writers"))))
|
|
||||||
val producers = List(Artist[Producer](Producer(row("producer"))))
|
|
||||||
val album = Some(Album(row("appears on"), "NA", null))
|
|
||||||
val rank = (Streak(row("streak")), Position(row("position")))
|
|
||||||
|
|
||||||
val s = Song(
|
val s = Song(
|
||||||
title = title,
|
title = row("title"),
|
||||||
description = Some(description),
|
description = Some(row("description")),
|
||||||
singer = singer,
|
artist = List(Artist(row("artist"))),
|
||||||
writer = writers,
|
writer = List(Writer(row("writers"))),
|
||||||
producer = producers,
|
producer = List(Producer(row("producer"))),
|
||||||
album = album,
|
album = Some(Album(row("appears on"), "NA")),
|
||||||
rank = rank
|
streak = Streak(row("streak")),
|
||||||
|
position = Position(row("position"))
|
||||||
)
|
)
|
||||||
|
|
||||||
// add the song to the TopSongs object
|
// add the song to the TopSongs object
|
||||||
topSongs = topSongs.addSong(s)
|
topSongs.addSong(s)
|
||||||
}
|
|
||||||
reader.close()
|
reader.close()
|
||||||
|
|
||||||
val foo: Boolean = topSongs.songs.exists(s => s.rank._1.streak.contains(1))
|
|
||||||
|
|
||||||
// print the songs
|
// print the songs
|
||||||
topSongs.printSongs()
|
topSongs.printSongs()
|
||||||
|
|
||||||
println("----------")
|
|
||||||
|
|
||||||
// print the number of songs by artist
|
|
||||||
val songsByArtist = topSongs.countSongsByArtist()
|
|
||||||
println("Number of songs by artist:")
|
|
||||||
songsByArtist.foreach {
|
|
||||||
case (artist, count) => println(s"$artist: $count")
|
|
||||||
}
|
|
||||||
|
|
||||||
println("----------")
|
|
||||||
|
|
||||||
// print the longest streak
|
|
||||||
val longestStreakSong = topSongs.longestStreak()
|
|
||||||
println(s"Longest streak: ${longestStreakSong.title} by ${longestStreakSong.singer.map(_.person.name).mkString(", ")} - ${longestStreakSong.rank._1.streak.getOrElse(0)} weeks")
|
|
||||||
|
|
||||||
println("----------")
|
|
||||||
|
|
||||||
// print the top N songs by weeks on chart
|
|
||||||
val topNSongs = topSongs.topNSongsByWeeks(5)
|
|
||||||
println("Top N songs by weeks on chart:")
|
|
||||||
topNSongs.foreach { song =>
|
|
||||||
println(s"${song.title} by ${song.singer.map(_.person.name).mkString(", ")} - ${song.rank._1.streak.getOrElse(0)} weeks")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user