Skip to content

Add ozans playlist concept exercise #1093

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

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions concepts/sets/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"blurb": "Store a collection of unique values",
"authors": ["kristinaborn"],
"contributors": []
}
93 changes: 93 additions & 0 deletions concepts/sets/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# About

In JavaScript, a set is a list-like data structure that can only contain unique primitives and/or object references.

Sets must be created using the set constructor; they can be initialized with values by passing an array to the constructor.

```javascript
const emptySet = new Set();
const setWithInitialValues = new Set(['hello', 'world']);
```

## Adding and removing elements

A value cannot be added to a set if it is [strictly equal][mdn-strict-equality] to any of the set's elements.

```javascript
const set = new Set();
set.add(10);
set.add(10); // 10 is strictly equal to 10, so it can't be added again
set.size;
//=> 1;

set.has(10);
//=> true
set.delete(10);
//=> true; 10 was removed
set.delete(10);
//=> false; 10 is not in the set, so nothing happened

const obj = { color: 'magenta' };
const eerilySimilarObj = { color: 'magenta' };

set.add(obj);
set.add(eerilySimilarObj); // obj and eerilySimilarObj reference different objects, so they can both be added
set.size;
//=> 2

set.clear();
set.size;
//=> 0
```

## Converting between sets and arrays

Arrays are converted to sets using the set constructor. Sets can be converted to arrays using either [spread syntax][mdn-spread-syntax] or `Array.from()`.

```javascript
const arrayWithDuplicates = [7, 3, 3, 9, 3];
const arrayWithoutDuplicates = [...new Set(arrayWithDuplicates)]; // [7, 3, 9]
const anotherArrayWithoutDuplicates = Array.from(new Set(arrayWithDuplicates)); // [7, 3, 9]
```

## Iteration

A set is a [keyed collection][mdn-keyed-collections], which means its elements are indexed by a key and can be iterated in insertion order. Because the _key_ and _value_ of a set element are the same, `Set.keys()` and `Set.values()` are interchangeable.

```javascript
const apples = ['Granny Smith', 'Honey Crisp', 'Royal Gala'];
const appleSet = new Set(apples);

for (let apple of appleSet.keys()) {
console.log(apple);
}
// 'Granny Smith'
// 'Honey Crisp'
// 'Royal Gala'

for (let apple of appleSet.values()) {
console.log(apple);
}
// 'Granny Smith'
// 'Honey Crisp'
// 'Royal Gala'

for (let apple of appleSet.entries()) {
console.log(apple);
}
// ['Granny Smith', 'Granny Smith']
// ['Honey Crisp', 'Honey Crisp']
// ['Royal Gala', 'Royal Gala']
```

## Lookup performance vs. arrays

When a set is chosen to store a collection of data (rather than an array), it's generally because the stored values need to be unique. However, since it's possible to enforce uniqueness with arrays too, it may not be obvious why that is.

The short answer is that sets are optimized for searching, and arrays are not. Array search methods (such as `Array.indexOf()`) have runtimes that increase linearly with the size of the array; that is, the more elements the array contains, the longer it takes to search it. Set search methods, on the other hand, have constant runtimes, meaning they stay the same regardless of the set's size.

In practice, though, this performance difference is negligible unless the collection contains a huge amount of data.

[mdn-strict-equality]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#strict_equality_using
[mdn-keyed-collections]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Keyed_collections
[mdn-spread-syntax]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
29 changes: 29 additions & 0 deletions concepts/sets/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Introduction

In JavaScript, a [set][mdn-sets] is a list-like structure containing unique values, which can be primitives and/or object references. Unlike an array, a set's elements cannot be accessed by index.

A value cannot be added to a set if it is [strictly equal][mdn-strict-equality] to any of the set's elements.

```javascript
const set = new Set();
const object = { color: 'lime green' };
const functionallyIdenticalObject = { color: 'lime green' };

set.add(object);
set.add('wow');
set.add(77);

console.log(set.size);
//=> 3

set.add(functionallyIdenticalObject); // added because functionallyIdenticalObject is not strictly equal to object
console.log(set.size);
//=> 4

set.add(77); // not added because 77 is strictly equal to 77
console.log(set.size);
//=> 4
```

[mdn-sets]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
[mdn-strict-equality]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#strict_equality_using
6 changes: 6 additions & 0 deletions concepts/sets/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set",
"description": "MDN: Sets"
}
]
14 changes: 14 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,20 @@
"concepts": ["promises"],
"prerequisites": ["arrays", "callbacks", "errors", "recursion"],
"status": "beta"
},
{
"slug": "ozans-playlist",
"name": "Ozan's Playlist",
"uuid": "50dba808-070a-4873-b223-d565381c3b4c",
"concepts": ["sets"],
"prerequisites": [
"array-destructuring",
"array-loops",
"comparison",
"rest-and-spread-operators",
"arrays"
],
"status": "beta"
}
],
"practice": [
Expand Down
35 changes: 35 additions & 0 deletions exercises/concept/ozans-playlist/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Hints

## 1. Remove duplicate tracks

- You can see an example of creating a set from an array in the [MDN docs][mdn-relation-to-arrays].
- Make sure to convert your set back into an array before returning it.
- Refer back to the [array destructuring concept][concept-array-destructuring] to recap how to use the spread operator.

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

- There is a [built-in method][mdn-set-has] for checking whether an element is in a set.

## 3. Add a track

- There is a [built-in method][mdn-set-add] for adding an element to a set.
- Make sure you're not manually calling `Set.has()` to check for the presence of the element before adding it; `Set.add()` takes care of that for you!

## 4. Delete a track

- There is a [built-in method][mdn-set-delete] for removing an element from a set.
- Make sure you're not manually calling `Set.has()` to check for the presence of the element before deleting it; `Set.delete()` takes care of that for you!

## 5. List unique artists

- There are [a few different ways][mdn-set-iteration] to iterate over a set.
- There is a [built-in method][mdn-string-split] for dividing a string into substrings.
- Refer back to the [array destructuring concept][concept-array-destructuring] to recap how to skip an element when destructuring an array.

[mdn-relation-to-arrays]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#relation_with_array_objects
[mdn-set-add]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/add
[mdn-set-delete]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/delete
[mdn-set-has]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has
[mdn-set-iteration]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#iterating_sets
[mdn-string-split]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split
[concept-array-destructuring]: /tracks/javascript/concepts/array-destructuring
85 changes: 85 additions & 0 deletions exercises/concept/ozans-playlist/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# 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 arrays, so he attempts to write some code that uses `Array.indexOf()` to check for the presence of a track before adding it to the playlist. Unfortunately, his program takes much too long to execute. He needs your help!

Coming to Ozan's aid, you are astonished to find that his playlist contains _half a million_ tracks. Perhaps you know of a different data structure that will allow you to manipulate the playlist more efficiently?

## 1. Remove duplicate tracks

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

```javascript
const playlist = [
'Court and Spark - Joni Mitchell',
'Big Yellow Taxi - Joni Mitchell',
'Court and Spark - Joni Mitchell',
];

removeDuplicates(playlist);
//=> ['Court and Spark - Joni Mitchell', 'Big Yellow Taxi - Joni Mitchell']
```

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

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

```javascript
const playlist = [
'The Fashion Show - Grace Jones',
'Dr. Funkenstein - Parliament',
];

hasTrack(playlist, 'Dr. Funkenstein - Parliament');
//=> true

hasTrack(playlist, 'Walking in the Rain - Grace Jones');
//=> false
```

## 3. Add a track

Implement the `addTrack` function, which takes a playlist and a track as _parameters_ and _returns_ a new playlist that includes the track.

```javascript
const playlist = ['Selma - Bijelo Dugme'];

addTrack(playlist, 'Atomic Dog - George Clinton');
//=> ['Selma - Bijelo Dugme', 'Atomic Dog - George Clinton']

addTrack(playlist, 'Selma - Bijelo Dugme');
//=> ['Selma - Bijelo Dugme', 'Atomic Dog - George Clinton']
```

## 4. Delete a track

Implement the `deleteTrack` function, which takes a playlist and a track as _parameters_ and _returns_ a new playlist that does not include the track.

```javascript
const playlist = [
'The Treasure - Fra Lippo Lippi',
'After the Fall - Klaus Nomi',
];

deleteTrack(playlist, 'The Treasure - Fra Lippo Lippi');
//=> ['After the Fall - Klaus Nomi']

deleteTrack(playlist, 'I Feel the Magic - Belinda Carlisle');
//=> ['After the Fall - Klaus Nomi']
```

## 5. List unique artists

Implement the `listArtists` function, which takes a playlist as a _parameter_ and _returns_ the list of unique artists in the playlist. Note that the names of the tracks are formatted like `<SONG> - <ARTIST>`.

```javascript
const playlist = [
'All Mine - Portishead',
'Sight to Behold - Devendra Banhart',
'Sour Times - Portishead',
];

listArtists(playlist);
//=> ['Portishead', 'Devendra Banhart']
```
29 changes: 29 additions & 0 deletions exercises/concept/ozans-playlist/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Introduction

In JavaScript, a [set][mdn-sets] is a list-like structure containing unique values, which can be primitives and/or object references. Unlike an array, a set's elements cannot be accessed by index.

A value cannot be added to a set if it is [strictly equal][mdn-strict-equality] to any of the set's elements.

```javascript
const set = new Set();
const object = { color: 'lime green' };
const functionallyIdenticalObject = { color: 'lime green' };

set.add(object);
set.add('wow');
set.add(77);

console.log(set.size);
//=> 3

set.add(functionallyIdenticalObject); // added because functionallyIdenticalObject is not strictly equal to object
console.log(set.size);
//=> 4

set.add(77); // not added because 77 is strictly equal to 77
console.log(set.size);
//=> 4
```

[mdn-sets]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
[mdn-strict-equality]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#strict_equality_using
14 changes: 14 additions & 0 deletions exercises/concept/ozans-playlist/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"root": true,
"extends": "@exercism/eslint-config-javascript",
"env": {
"jest": true
},
"overrides": [
{
"files": [".meta/proof.ci.js", ".meta/exemplar.js", "*.spec.js"],
"excludedFiles": ["custom.spec.js"],
"extends": "@exercism/eslint-config-javascript/maintainers"
}
]
}
3 changes: 3 additions & 0 deletions exercises/concept/ozans-playlist/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
yarn-error.log

10 changes: 10 additions & 0 deletions exercises/concept/ozans-playlist/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"blurb": "Use sets to avoid repeating tracks in a playlist",
"authors": ["kristinaborn"],
"contributors": [],
"files": {
"solution": ["ozans-playlist.js"],
"test": ["ozans-playlist.spec.js"],
"exemplar": [".meta/exemplar.js"]
}
}
45 changes: 45 additions & 0 deletions exercises/concept/ozans-playlist/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Design

## Learning objectives

- Know how to use a set to remove duplicate elements from an array
- Know how to convert between a set and an array
- Know how to check whether a value is in a set
- Know how to add and remove elements from a set
- Know how to iterate over a set
- Understand when a set might be preferable to an array

## Out of Scope

- Implementing common set operations like `union` and `difference`
- `WeakSet`

## Concepts

The Concepts this exercise unlocks are:

- `sets`

## Prerequisites

- `array-destructuring` because examples use array destructuring
- `comparison` because this is where equality is explained
- `array-loops` because it introduces the for-of loop
- `rest-and-spread-operators`
- `arrays`

## Analyzer

This exercise could benefit from the following rules in the [analyzer][analyzer]:

For all tasks, verify that the student actually used a `Set`.

1. `addTrack`

- Verify that there was no redundant `Set.has()` call

2. `deleteTrack`

- Verify that there was no redundant `Set.has()` call

[analyzer]: https://github.com/exercism/javascript-analyzer
Loading