Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ADD] Solution implemented with Java, Ruby and Haskell #221

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions submissions/Bltzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target/
17 changes: 17 additions & 0 deletions submissions/Bltzz/Haskell/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
dist
cabal-dev
*.o
*.hi
*.chi
*.chs.h
*.dyn_o
*.dyn_hi
.hpc
.hsenv
.cabal-sandbox/
cabal.sandbox.config
*.prof
*.aux
*.hp
.stack-work/

18 changes: 18 additions & 0 deletions submissions/Bltzz/Haskell/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Bltzz Solution [Haskell]

## Prereq:

```
cabal update
cabal install csv-conduit containers
```

## Build
```
ghc solutions.hs
```

## Usage:
```
solution.exe "columnNameOfChoice"
```
954 changes: 954 additions & 0 deletions submissions/Bltzz/Haskell/data/spotify-clean.CSV

Large diffs are not rendered by default.

Binary file added submissions/Bltzz/Haskell/solution.exe
Binary file not shown.
190 changes: 190 additions & 0 deletions submissions/Bltzz/Haskell/solution.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import Control.Monad
import Data.Char (toLower)
import Data.List (maximumBy, nub)
import Data.Map qualified as M
import Data.Ord (comparing)
import System.Environment (getArgs)
import Text.CSV (CSV, parseCSV)

data DataObject = DataObject
{ trackName :: String,
artistName :: String,
artistCount :: String,
releasedYear :: String,
releasedMonth :: String,
releasedDay :: String,
inSpotifyPlaylists :: String,
inSpotifyCharts :: String,
streams :: String,
inApplePlaylists :: String,
inAppleCharts :: String,
inDeezerPlaylists :: String,
inDeezerCharts :: String,
inShazamCharts :: String,
bpm :: String,
key :: String,
mode :: String,
danceabilityPercentage :: String,
valencePercentage :: String,
energyPercentage :: String,
acousticnessPercentage :: String,
instrumentalnessPercentage :: String,
livenessPercentage :: String,
speechinessPercentage :: String
}
deriving (Show)

newtype Analyzer = Analyzer
{ inputData :: [DataObject]
}

-- Read CSV File to a list of data objects
readCSVContentSkipFirstLine :: FilePath -> IO [DataObject]
readCSVContentSkipFirstLine file = do
csvData <- readFile file
case parseCSV file csvData of
Left err -> do
putStrLn $ "Error parsing CSV: " ++ show err
pure []
Right rows -> do
let dataObjects = map createDataObject (tail rows)
-- Print individual fields for validation or debugging
-- mapM_ printFields dataObjects
-- Return the list of DataObjects
pure dataObjects

-- Right rows -> pure $ map createDataObject (tail rows)

-- Print individual fields of a DataObject
printFields :: DataObject -> IO ()
printFields obj = do
putStrLn $ "Track Name: " ++ trackName obj
putStrLn $ "Artist Name: " ++ artistName obj
-- Print other fields as needed
putStrLn "-------------------------"

-- Create single data object -> Mapp Data from CSV Row into the DataObject Struct
createDataObject :: [String] -> DataObject
createDataObject dataRow =
DataObject
{ trackName = dataRow !! 0,
artistName = dataRow !! 1,
artistCount = dataRow !! 2,
releasedYear = dataRow !! 3,
releasedMonth = dataRow !! 4,
releasedDay = dataRow !! 5,
inSpotifyPlaylists = dataRow !! 6,
inSpotifyCharts = dataRow !! 7,
streams = dataRow !! 8,
inApplePlaylists = dataRow !! 9,
inAppleCharts = dataRow !! 10,
inDeezerPlaylists = dataRow !! 11,
inDeezerCharts = dataRow !! 12,
inShazamCharts = dataRow !! 13,
bpm = dataRow !! 14,
key = dataRow !! 15,
mode = dataRow !! 16,
danceabilityPercentage = dataRow !! 17,
valencePercentage = dataRow !! 18,
energyPercentage = dataRow !! 19,
acousticnessPercentage = dataRow !! 20,
instrumentalnessPercentage = dataRow !! 21,
livenessPercentage = dataRow !! 22,
speechinessPercentage = dataRow !! 23
}

-- Create a mapping of the input column string to a lambda expression with
-- a) the function or record field accessor that can be applied to the obj argument
-- b) the const int 1 (needed for solution 3, represents the counter for objects (starts at 1))
mapColumnName :: String -> Maybe (DataObject -> (String, Int))
mapColumnName columnName =
case map toLower columnName of
"track_name" -> Just (\obj -> (trackName obj, 1))
"artist(s)_name" -> Just (\obj -> (artistName obj, 1))
"artist_count" -> Just (\obj -> (artistCount obj, 1))
"released_year" -> Just (\obj -> (releasedYear obj, 1))
"released_month" -> Just (\obj -> (releasedMonth obj, 1))
"released_day" -> Just (\obj -> (releasedDay obj, 1))
"in_spotify_playlists" -> Just (\obj -> (inSpotifyPlaylists obj, 1))
"in_spotify_charts" -> Just (\obj -> (inSpotifyCharts obj, 1))
"streams" -> Just (\obj -> (streams obj, 1))
"in_apple_playlists" -> Just (\obj -> (inApplePlaylists obj, 1))
"in_apple_charts" -> Just (\obj -> (inAppleCharts obj, 1))
"in_deezer_playlists" -> Just (\obj -> (inDeezerPlaylists obj, 1))
"in_deezer_charts" -> Just (\obj -> (inDeezerCharts obj, 1))
"in_shazam_charts" -> Just (\obj -> (inShazamCharts obj, 1))
"bpm" -> Just (\obj -> (bpm obj, 1))
"key" -> Just (\obj -> (key obj, 1))
"mode" -> Just (\obj -> (mode obj, 1))
"danceability_%" -> Just (\obj -> (danceabilityPercentage obj, 1))
"valence_%" -> Just (\obj -> (valencePercentage obj, 1))
"energy_%" -> Just (\obj -> (energyPercentage obj, 1))
"acousticness_%" -> Just (\obj -> (acousticnessPercentage obj, 1))
"instrumentalness_%" -> Just (\obj -> (instrumentalnessPercentage obj, 1))
"liveness_%" -> Just (\obj -> (livenessPercentage obj, 1))
"speechiness_%" -> Just (\obj -> (speechinessPercentage obj, 1))
_ -> Nothing

main :: IO ()
main = do
args <- getArgs
if null args
then
putStrLn "For [Solution 3], please provide the column name as command line arg."
>> putStrLn "Possible values: track_name, artist(s)_name, artist_count, released_year, released_month, released_day,"
>> putStrLn "in_spotify_playlists, in_spotify_charts, streams, in_apple_playlists, in_apple_charts,"
>> putStrLn "in_deezer_playlists, in_deezer_charts, in_shazam_charts, bpm, key, mode,"
>> putStrLn "danceability_%, valence_%, energy_%, acousticness_%,"
>> putStrLn "instrumentalness_%, liveness_%, speechiness_%"
else do
let columnName = head args
let mappedColumn = mapColumnName columnName
fileData <- readCSVContentSkipFirstLine "data/spotify-clean.csv"
let analyzer = Analyzer fileData

-- Solution 1
let numberOfSongs = getNumberOfSongsInFile analyzer
putStrLn $ "[Solution 1]: Number of unique songs: " ++ show numberOfSongs

-- Solution 2
let numberOfSongsWithKeyE = getNumberOfSongsInFileWithKeyE analyzer
putStrLn $ "[Solution 2]: Number of unique songs with Key 'E': " ++ show numberOfSongsWithKeyE

-- Solution 3
case mappedColumn of
Just column -> findMostCommonValueInSpecifiedColumn analyzer columnName column
Nothing -> putStrLn "Invalid column name provided"

-- counts all unique names in the trackName column
getNumberOfSongsInFile :: Analyzer -> Int
getNumberOfSongsInFile analyzer = length $ nub $ map trackName $ inputData analyzer

-- counts all unique values with the exact letter E in the key field
getNumberOfSongsInFileWithKeyE :: Analyzer -> Int
getNumberOfSongsInFileWithKeyE analyzer = length $ filter hasKeyE (inputData analyzer)
where
hasKeyE :: DataObject -> Bool
hasKeyE dataObject = key dataObject == "E"

-- Finds the most common value in a user specified column
-- Inputs:
-- 1) The user input column name
-- 2) the Lambda expression based on the input. Example: (\obj -> (trackName obj, 1))
findMostCommonValueInSpecifiedColumn :: Analyzer -> String -> (DataObject -> (String, Int)) -> IO ()
findMostCommonValueInSpecifiedColumn analyzer columnInputName column = do
-- apply the lambda expression to the inputData
let songCounterMap = map column $ inputData analyzer
-- foldr is a higher-order function that folds a binary function over a list from right to left. It takes three arguments:
-- 1) The binary function that combines an element of the list and an accumulator.
-- 2) The initial accumulator value.
-- 3) The list to fold.
-- The lambda function \(key, count) acc -> M.insertWith (+) key count acc takes a tuple (key, count) and an accumulator acc.
-- It inserts the tuple into the accumulator map (M.insertWith (+) key count acc). If the key is already present in the map,
-- it adds the current count to the existing count using the (+) function.
-- M.empty is the initial accumulator value, an empty Data.Map.
-- songCounterMap is the list of tuples that are being folded.
let groupedMap = foldr (\(key, count) acc -> M.insertWith (+) key count acc) M.empty songCounterMap
-- select most frequent values based on count - store it in tupel
let (highScoreKey, highScore) = maximumBy (comparing snd) $ M.toList groupedMap
-- print result
putStrLn $ "[Solution 3]: HighScore for column [" ++ columnInputName ++ "]: " ++ highScoreKey ++ ": " ++ show highScore
20 changes: 20 additions & 0 deletions submissions/Bltzz/Java/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/target/
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar

# Eclipse m2e generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath

.settings
Binary file added submissions/Bltzz/Java/Analyzer.jar
Binary file not shown.
21 changes: 21 additions & 0 deletions submissions/Bltzz/Java/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Bltzz solution

The implementation is a Maven Project, with an automated build.

### Build
```cmd
cd /path/to/file-io/submissions/Bltzz/Java
mvn clean install

move target\Analyzer-jar-with-dependencies.jar Analyzer.jar
```

### Usage

```cmd
cd /path/to/file-io/submissions/Bltzz

java -jar Analyzer.jar --column [theNameOfTheCSVColumnYouWantToCountForSolution3]

```