Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@
"vectors"
],
"status": "wip"
},
{
"slug": "ozans-playlist",
"name": "Ozans Playlist",
"uuid": "323ff052-9238-4299-944c-c231c939ff9f",
"concepts": [
"set-operations"
],
"prerequisites": [
"vectors"
],
"status": "wip"
}
],
"practice": [
Expand Down
18 changes: 18 additions & 0 deletions exercises/concept/ozans-playlist/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Hints

## General

These are all short and simple tasks.
If you find yourself writing several lines of code in a function, please stop and read the introduction.

## 3. Add tracks
Comment on lines +5 to +8
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if we need to have a complete list (from 1 to 5 here). I would venture a guess that it's not necessary, but that really is just a guess.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Point noted, but I'm curious to test whether hints need to be numbered consecutively from 1. Is it OK to merge this as-is and see how it looks?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May as well give it a look to see how the website reacts.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems absolutely fine. I think the Rails backend just looks for level-2 headers (starting with ## ).


- Return tracks that are in either the playlist or the tracks to add.

## 4. Delete tracks

- Tracks which are in the playlist but not the tracks to delete.

## 5. Compare playlists

- Tracks which are in both playlists.
86 changes: 86 additions & 0 deletions exercises/concept/ozans-playlist/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Instructions

Ozan is putting together a playlist for an upcoming roadtrip. He doesn"t want to hear the same track more than once, but the playlist has gotten so long that he"s having trouble remembering which tracks have already been added.

The API for Ozan"s music player only knows how to work with vectors. No explicit loops or if/else logic are possible. He needs your help!

## 1. Remove duplicate tracks

Implement the `remove_duplicates` function, which takes a playlist as a _parameter_ and _returns_ a new playlist where all the tracks are unique.

```R
> playlist <- c(
"Court and Spark - Joni Mitchell",
"Big Yellow Taxi - Joni Mitchell",
"Court and Spark - Joni Mitchell"
)

> remove_duplicates(playlist)
[1] "Court and Spark - Joni Mitchell" "Big Yellow Taxi - Joni Mitchell"
```

## 2. Check whether a track has already been added

Implement the `has_track` function, which takes a playlist and a track as _parameters_ and _returns_ a boolean that indicates whether the playlist contains the track.

```R
> playlist <- c(
"The Fashion Show - Grace Jones",
"Dr. Funkenstein - Parliament"
)

> has_track(playlist, "Dr. Funkenstein - Parliament")
[1] TRUE

> has_track(playlist, "Walking in the Rain - Grace Jones")
[1] FALSE
```

## 3. Add tracks

Implement the `add_tracks` function, which takes a playlist and a vector of one or more tracks as _parameters_ and _returns_ a new playlist that includes the tracks.

```R
> playlist <- c("Selma - Bijelo Dugme")

> playlist <- add_tracks(playlist, "Atomic Dog - George Clinton")
> playlist
[1] "Selma - Bijelo Dugme" "Atomic Dog - George Clinton"

> add_tracks(playlist, "Selma - Bijelo Dugme")
[1] "Selma - Bijelo Dugme" "Atomic Dog - George Clinton"

> add_tracks(playlist, c("The Chain - Fleetwood Mac", "Selma - Bijelo Dugme"))
[1] "Selma - Bijelo Dugme" "Atomic Dog - George Clinton" "The Chain - Fleetwood Mac"

```

## 4. Delete tracks

Implement the `delete_tracks` function, which takes a playlist and one or more tracks as _parameters_ and _returns_ a new playlist that does not include the tracks.

```R
> playlist <- c(
"The Treasure - Fra Lippo Lippi",
"After the Fall - Klaus Nomi"
)

> playlist <- delete_tracks(playlist, "The Treasure - Fra Lippo Lippi")
> playlist
[1] "After the Fall - Klaus Nomi"

> delete_tracks(playlist, "I Feel the Magic - Belinda Carlisle")
[1] "After the Fall - Klaus Nomi"
```

## 5. Compare playlists

Ozan meets a new friend and wonders how similar their tastes are.
Implement the `find_common_tracks` function, which takes two playlists and returns a new playlist containing only tracks present in both.

```R
> playlist_1 <- c("Karma - Taylor Swift", "Tired - Adele")
> playlist_2 <- c("Andromeda - Weyes Blood", "Karma - Taylor Swift")
> find_common_tracks(playlist_1, playlist_2)
[1] "Karma - Taylor Swift"
````
36 changes: 36 additions & 0 deletions exercises/concept/ozans-playlist/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Introduction

R has no separate Set datatype, instead using a variety of functions to perform similar operations on vectors.

The `%in%` operator tests for set membership.

```R
2 %in% 1:10 # TRUE
12 %in% 1:10 # FALSE
```

Relevant functions include `unique` (to remove duplicates), plus `union()`, `intersect()`, `setdiff()` and `setequal()` to operate on pairs of sets.

```R
> set_1 <- c("a", "b", "c", "b", "a")
> unique(set_1) # deduplicate
[1] "a" "b" "c"

> set_2 <- c('a', "c", "d")
> union(set_1, set_2) # values in either set
[1] "a" "b" "c" "d"

> intersect(set_1, set_2) # values in both sets
[1] "a" "c"

> setdiff(set_1, set_2) # values in set_1 but not set_2
[1] "b"

> setdiff(set_2, set_1) # values in set_2 but not set_1
[1] "d"

setequal(set_1, c("c", "b", "a", "b")) # are elements the same after deduplication?
[1] TRUE
```

In each case, `set_1` and `set_2` remain unchanged.
20 changes: 20 additions & 0 deletions exercises/concept/ozans-playlist/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"authors": [
"colinleach"
],
"files": {
"solution": [
"ozans-playlist.R"
],
"test": [
"test_ozans-playlist.R"
],
"exemplar": [
".meta/exemplar.R"
]
},
"forked_from": [
"javascript/ozans-playlist"
],
"blurb": "Use set operations to manage a playlist"
}
24 changes: 24 additions & 0 deletions exercises/concept/ozans-playlist/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Design

## Goal

The goal of this exercise is to introduce basic set operations in R.

## Learning objectives

- Understand that base R uses vectors for set operations, with no dediicated class.
- Understand the use of `%in%`, `unique()`, `union()`, `intersect()` and `setdiff()`.

## Out of scope

- Use of the `hashtable` package (or similar), though this is mentioned in `set-operations/about.md` which unlocks on completing this exercise.

## Concepts

The Concepts this exercise unlocks are:

- None.

## Prerequisites

- `vectors`
19 changes: 19 additions & 0 deletions exercises/concept/ozans-playlist/.meta/exemplar.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
remove_duplicates <- function(playlist) {
unique(playlist)
}

has_track <- function(playlist, track) {
track %in% playlist
}

add_tracks <- function(playlist, tracks) {
union(playlist, tracks)
}

delete_tracks <- function(playlist, tracks) {
setdiff(playlist, tracks)
}

find_common_tracks <- function(playlist_1, playlist_2) {
intersect(playlist_1, playlist_2)
}
14 changes: 14 additions & 0 deletions exercises/concept/ozans-playlist/ozans-playlist.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
remove_duplicates <- function(playlist) {
}

has_track <- function(playlist, track) {
}

add_tracks <- function(playlist, tracks) {
}

delete_tracks <- function(playlist, tracks) {
}

find_common_tracks <- function(playlist_1, playlist_2) {
}
110 changes: 110 additions & 0 deletions exercises/concept/ozans-playlist/test_ozans-playlist.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
source("./ozans-playlist.R")
library(testthat)

# 1) remove_duplicates

test_that("1. works for a non-empty playlist", {
track_1 <- "Two Paintings and a Drum - Carl Cox"
track_2 <- "Leash Called Love - The Sugarcubes"
playlist <- c(track_1, track_2, track_1)
expected <- c(track_1, track_2)
expect_equal(remove_duplicates(playlist), expected)
})

test_that("1. works for an empty playlist", {
playlist <- c()
expected <- c()
expect_equal(remove_duplicates(playlist), expected)
})

# 2) has_track

test_that("2. returns true when the track is in the playlist", {
track_1 <- "Big Science - Laurie Anderson"
track_2 <- "Tightrope - Laurie Anderson"
playlist <- c(track_1, track_2)
expect_equal(has_track(playlist, track_1), TRUE)
})

test_that("2. returns false when the track is not in the playlist", {
track_1 <- "Big Science - Laurie Anderson"
track_2 <- "Tightrope - Laurie Anderson"
playlist <- c(track_2)
expect_equal(has_track(playlist, track_1), FALSE)
})

# 3) add_tracks

test_that("3. adds tracks that are not already in the playlist", {
track_1 <- "Jigsaw Feeling - Siouxsie and the Banshees"
track_2 <- "Feeling Good - Nina Simone"
playlist <- c()
expected <- c(track_1)
expect_equal(add_tracks(playlist, track_1), expected)
})

test_that("3. does not add a track that is already in the playlist", {
track_1 <- "Jigsaw Feeling - Siouxsie and the Banshees"
track_2 <- "Feeling Good - Nina Simone"
playlist <- c(track_1, track_2)
expected <- c(track_1, track_2)
expect_equal(add_tracks(playlist, track_1), expected)
})

test_that("3. add multiple tracks, some already in the playlist", {
track_1 <- "Jigsaw Feeling - Siouxsie and the Banshees"
track_2 <- "Feeling Good - Nina Simone"
track_3 <- "I Was an Eagle - Laura Marling"
playlist <- c(track_1, track_2)
expected <- c(track_1, track_2, track_3)
expect_equal(add_tracks(playlist, c(track_1, track_3)), expected)
})

# 4) delete_tracks

test_that("4. works if the track is present in the playlist", {
track_1 <- "Ancestors - Tanya Tagaq"
track_2 <- "Take This Hammer - Lead Belly"
playlist <- c(track_1, track_2)
expected <- c(track_2)
expect_equal(delete_tracks(playlist, track_1), expected)
})

test_that("4. works if the track is not present in the playlist", {
track_1 <- "Ancestors - Tanya Tagaq"
track_2 <- "Take This Hammer - Lead Belly"
playlist <- c(track_2)
expected <- c(track_2)
expect_equal(delete_tracks(playlist, track_1), expected)
})

test_that("4. works for multiple tracks if some present in the playlist", {
track_1 <- "Ancestors - Tanya Tagaq"
track_2 <- "Take This Hammer - Lead Belly"
track_3 <- "With Or Without You - U2"
playlist <- c(track_1, track_2)
expected <- c(track_2)
expect_equal(delete_tracks(playlist, c(track_1, track_3)), expected)
})

# 5) find_common_tracks

test_that("5. works when there is partial overlap", {
track_1 <- "Ancestors - Tanya Tagaq"
track_2 <- "Take This Hammer - Lead Belly"
track_3 <- "With Or Without You - U2"
playlist_1 <- c(track_1, track_2)
playlist_2 <- c(track_2, track_3)
expected <- c(track_2)
expect_equal(find_common_tracks(playlist_1, playlist_2), expected)
})

test_that("5. works when there nothing in common", {
track_1 <- "Paranoid - Black Sabbath"
track_2 <- "Overkill - Motörhead"
track_3 <- "Gurre-Lieder - Schönberg"
track_4 <- "Gymnopédies - Satie"
playlist_1 <- c(track_1, track_2)
playlist_2 <- c(track_3, track_4)
expect_equal(length(find_common_tracks(playlist_1, playlist_2)), 0)
})