diff --git a/concepts/array-destructuring/about.md b/concepts/array-destructuring/about.md index 4a550871ca..481e84157a 100644 --- a/concepts/array-destructuring/about.md +++ b/concepts/array-destructuring/about.md @@ -1,6 +1,6 @@ # About -Array [destructuring assignment][array_destructuring_docs] is a concise way of extracting values from an array. Its syntax is similar to an [array literal][array_literal_resource] expression, but on the left-hand side of the assignment instead of the right. +Array [destructuring assignment][mdn-array-destructuring] is a concise way of extracting values from an array. Its syntax is similar to an [array literal][mdn-array-literal] expression, but on the left-hand side of the assignment instead of the right. ```javascript const frenchNumbers = ['quatre-vingts', 'quatre-vingt-dix', 'cent']; @@ -14,6 +14,8 @@ french100; // => 'cent' ``` +## Re-assignment + Because variables are mapped to values in the array by position, destructuring syntax can be used to assign or re-assign multiple variables in a single expression. ```javascript @@ -40,6 +42,8 @@ c; // => 'purple' ``` +## Skipping assignment + The syntax allows skipping values when mapping, for example to ignore specific positions in the array. In the example below, imagine we have a `getUserInfo` function that returns an array containing a user's first name, last name, and street address. @@ -53,6 +57,8 @@ streetAddress; // => "Sunny Lane 523" ``` +## Dropping values + The assignment is also not required to use all the values. ```javascript @@ -65,6 +71,8 @@ lastName; // => "Noir" ``` +## Taking more values than available + It's even possible to extract _more_ values than the array contains; the leftover variables will be assigned `undefined`. This may be useful when the amount of values isn't known ahead of time. ```javascript @@ -84,6 +92,8 @@ fourth; // => undefined ``` +## Default values + The array destructuring assignment can provide _default values_ in case there is none in the source array. ```javascript @@ -96,4 +106,10 @@ fourth; // => undefined ``` +## Related concepts + +[concept:javascript/object-destructuring]() [concept:javascript/rest-and-spread]() + +[mdn-array-destructuring]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment +[mdn-array-literal]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#Creating_an_array diff --git a/concepts/object-destructuring/.meta/config.json b/concepts/object-destructuring/.meta/config.json new file mode 100644 index 0000000000..3500286e76 --- /dev/null +++ b/concepts/object-destructuring/.meta/config.json @@ -0,0 +1,5 @@ +{ + "blurb": "Object destructuring is a concise way of extracting properties from an object.", + "authors": ["SleeplessByte"], + "contributors": [] +} diff --git a/concepts/object-destructuring/about.md b/concepts/object-destructuring/about.md new file mode 100644 index 0000000000..b4fd7ce627 --- /dev/null +++ b/concepts/object-destructuring/about.md @@ -0,0 +1,129 @@ +# About + +Object [destructuring][mdn-object-destructuring] is a concise way of extracting properties from an object. +Its syntax is similar to an [object literal][mdn-object-literal] expression, but on the left-hand side of the assignment instead of the right. + +```javascript +const weather = { + sun: 'â˜€ī¸', + sun_behind_small_cloud: 'đŸŒ¤ī¸', + sun_behind_cloud: '⛅', + sun_behind_large_cloud: 'đŸŒĨī¸', + sun_behind_rain_cloud: 'đŸŒĻī¸', + cloud: 'â˜ī¸', + cloud_with_rain: 'đŸŒ§ī¸', + cloud_with_snow: 'đŸŒ¨ī¸', + cloud_with_lightning: 'đŸŒŠī¸', + cloud_with_lightning_and_rain: 'â›ˆī¸', +}; + +const { sun, cloud, cloud_with_lightning } = weather; + +sun; +// => 'â˜€ī¸' + +cloud; +// => 'â˜ī¸' + +cloud_with_lightning; +// => 'đŸŒŠī¸' +``` + +## Renaming in assignment + +The syntax can extract the properties by their key like `sun`, `cloud`, and `cloud_with_lightning`, but can also pick a different name for the variable: + +```javascript +const { sun: sunny, cloud: cloudy, cloud_with_rain: rainy } = weather; + +typeof cloud_with_rain; +// => 'undefined' + +typeof rainy; +// => 'string' + +rainy; +// => đŸŒ§ī¸ +``` + +The assignment is also not required to use all the values. + +## Default values + +The object destructuring assignment can provide _default values_ in case there is none in the source object: + +```javascript +const { sun = '🌞', tornado = 'đŸŒĒī¸', cloud_with_snow: snowy = 'â„ī¸' } = weather; + +sun; +// => 'â˜€ī¸' + +tornado; +// => 'đŸŒĒī¸' + +snowy; +// => 'đŸŒ¨ī¸' +``` + +The following is observed: + +- `sun` has extracted from the object `weather` without replacing it as it is present in the object `weather`, +- `tornado` does not exist in the object `weather`, so the default value was used, +- `cloud_with_snow` was extracted as the variable `snowy`, without replacing it, as `cloud_with_snow` is present in the object `weather`. + +## Destructuring function parameters + +The `weather` object has a lot of properties. +It is possible to directly extract one or multiple properties from this object when it's passed to a function: + +```javascript +function weatherReport({ sun }) { + console.log(sun); +} + +weatherReport(sun); +// => 'â˜€ī¸' +``` + +## Destructuring `for of` iteration + +When iterating over an `array` (or other iterable), and the items are objects, it is possible to destructure inside the `for (...) of iterable` statement: + +```javascript +const { sun: sunny, cloud: cloudy, cloud_with_rain: rainy } = weather; + +const prediction = [ + { + chance: 0.8, + weather: sunny, + description: 'There is a 80% chance it will remain sunny.', + }, + { + chance: 0.15, + weather: cloudy, + description: + 'There is a 15% chance the clouds will keep the sun from poking through.', + }, + { + chance: 0.05, + weather: rainy, + description: 'There is a small chance of rain.', + }, +]; + +for (const { weather: symbol, description } of prediction) { + console.log(`${symbol}: ${description}`); +} + +// 'â˜€ī¸: There is a 80% chance it will remain sunny.' +// 'â˜ī¸: There is a 15% chance the clouds will keep the sun from poking through.' +// 'đŸŒ§ī¸: There is a small chance of rain.' +``` + +## Related concepts + +[concept:javascript/array-destructuring]() +[concept:javascript/rest-and-spread]() + +[mdn-object-destructuring]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring#object_destructuring +[mdn-object-literal]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer diff --git a/concepts/object-destructuring/introduction.md b/concepts/object-destructuring/introduction.md new file mode 100644 index 0000000000..77f1b8230a --- /dev/null +++ b/concepts/object-destructuring/introduction.md @@ -0,0 +1,31 @@ +# Introduction + +JavaScript's object destructuring syntax is a concise way to extract properties from an object and assign them to distinct variables. + +In this example, weather symbols are extracted from the object `weather`: + +```javascript +const weather = { + sun: 'â˜€ī¸', + sun_behind_small_cloud: 'đŸŒ¤ī¸', + sun_behind_cloud: '⛅', + sun_behind_large_cloud: 'đŸŒĨī¸', + sun_behind_rain_cloud: 'đŸŒĻī¸', + cloud: 'â˜ī¸', + cloud_with_rain: 'đŸŒ§ī¸', + cloud_with_snow: 'đŸŒ¨ī¸', + cloud_with_lightning: 'đŸŒŠī¸', + cloud_with_lightning_and_rain: 'â›ˆī¸', +}; + +const { sun, cloud, cloud_with_lightning } = weather; + +sun; +// => 'â˜€ī¸' + +cloud; +// => 'â˜ī¸' + +cloud_with_lightning; +// => 'đŸŒŠī¸' +``` diff --git a/concepts/object-destructuring/links.json b/concepts/object-destructuring/links.json new file mode 100644 index 0000000000..b4446d2b1e --- /dev/null +++ b/concepts/object-destructuring/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring#object_destructuring", + "description": "MDN: Object destructuring" + }, + { + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer", + "description": "MDN: Object initializer (and literals)" + } +] diff --git a/config.json b/config.json index d054b5c0c8..6a160f8242 100644 --- a/config.json +++ b/config.json @@ -175,7 +175,8 @@ "name": "Elyses Destructured Enchantments", "uuid": "d9b5cd13-2f2b-4034-a571-e66c847ed6f8", "concepts": [ - "array-destructuring" + "array-destructuring", + "object-destructuring" ], "prerequisites": [ "arrays", @@ -192,7 +193,9 @@ "rest-and-spread" ], "prerequisites": [ - "array-destructuring" + "functions", + "array-destructuring", + "object-destructuring" ], "status": "beta" }, @@ -2714,6 +2717,11 @@ "slug": "closures", "name": "Closures" }, + { + "uuid": "ef1e44db-5fd0-4550-9135-7522303e7e42", + "slug": "comparison", + "name": "Comparison" + }, { "uuid": "2d0b9f1f-c135-4014-b87c-25b081387002", "slug": "conditionals", @@ -2729,6 +2737,26 @@ "slug": "errors", "name": "Errors" }, + { + "uuid": "be301f37-6cf4-4688-949e-75a8976a91e3", + "slug": "for-loops", + "name": "For Loops" + }, + { + "uuid": "66264917-2d3e-42e9-806c-0fa740852f0f", + "slug": "functions", + "name": "Functions" + }, + { + "uuid": "a2347a19-1e44-449b-9741-94eda00d8ba7", + "slug": "increment-decrement", + "name": "Increment/Decrement" + }, + { + "uuid": "c890e216-5acb-4fb8-8081-61eb78eabe87", + "slug": "inheritance", + "name": "Inheritance" + }, { "uuid": "008f1c88-7c14-48b2-a88d-49ecb5e3b122", "slug": "null-undefined", @@ -2739,75 +2767,60 @@ "slug": "numbers", "name": "Numbers" }, + { + "uuid": "68dd89d3-13b8-49f5-b493-ccfdc82a073c", + "slug": "objects", + "name": "Objects" + }, + { + "uuid": "38bb937b-b9d6-4550-8337-5e4f2623837a", + "slug": "object-destructuring", + "name": "Object Destructuring" + }, { "uuid": "b3aa57d9-74b2-4d04-a673-ae2402630d8b", "slug": "promises", "name": "Promises" }, + { + "uuid": "e0ff7a2b-d6f7-4513-9589-3e6854e14415", + "slug": "classes", + "name": "Prototypes & Classes" + }, { "uuid": "cfbc96fa-717e-4f29-a91d-760ebea88822", "slug": "recursion", "name": "Recursion" }, + { + "uuid": "e5695bba-50c1-4c55-af3e-2444883bd83b", + "slug": "regular-expressions", + "name": "Regular Expressions" + }, { "uuid": "efc895b2-8420-44f1-a385-5c637286f797", "slug": "rest-and-spread", "name": "Rest and Spread" }, { - "uuid": "19085ad2-038a-4e08-ad34-47ff2a78fec6", - "slug": "template-strings", - "name": "Template Strings" + "uuid": "84e55c29-d403-4a90-8a2a-9960feae8ff3", + "slug": "sets", + "name": "Sets" }, { "uuid": "7d5c1533-c7cf-418e-b0f2-080da1e5bdc5", "slug": "strings", "name": "Strings" }, - { - "uuid": "ef1e44db-5fd0-4550-9135-7522303e7e42", - "slug": "comparison", - "name": "Comparison" - }, - { - "uuid": "a2347a19-1e44-449b-9741-94eda00d8ba7", - "slug": "increment-decrement", - "name": "Increment/Decrement" - }, - { - "uuid": "be301f37-6cf4-4688-949e-75a8976a91e3", - "slug": "for-loops", - "name": "For Loops" - }, - { - "uuid": "4e68e39a-e36c-4d2d-8714-eb6482e31ff5", - "slug": "while-loops", - "name": "While Loops" - }, { "uuid": "d5d54931-b2a7-4b1a-a593-ad85a2810f2f", "slug": "conditionals-switch", "name": "Switch Statement" }, { - "uuid": "68dd89d3-13b8-49f5-b493-ccfdc82a073c", - "slug": "objects", - "name": "Objects" - }, - { - "uuid": "84e55c29-d403-4a90-8a2a-9960feae8ff3", - "slug": "sets", - "name": "Sets" - }, - { - "uuid": "66264917-2d3e-42e9-806c-0fa740852f0f", - "slug": "functions", - "name": "Functions" - }, - { - "uuid": "cbad4d23-a9d8-4370-add2-f4416a4df027", - "slug": "type-conversion", - "name": "Type Conversion" + "uuid": "19085ad2-038a-4e08-ad34-47ff2a78fec6", + "slug": "template-strings", + "name": "Template Strings" }, { "uuid": "168cb8e8-c4f9-4e10-9d79-bffc77b86bbf", @@ -2815,19 +2828,14 @@ "name": "Ternary Operator" }, { - "uuid": "c890e216-5acb-4fb8-8081-61eb78eabe87", - "slug": "inheritance", - "name": "Inheritance" - }, - { - "uuid": "e0ff7a2b-d6f7-4513-9589-3e6854e14415", - "slug": "classes", - "name": "Prototypes & Classes" + "uuid": "cbad4d23-a9d8-4370-add2-f4416a4df027", + "slug": "type-conversion", + "name": "Type Conversion" }, { - "uuid": "e5695bba-50c1-4c55-af3e-2444883bd83b", - "slug": "regular-expressions", - "name": "Regular Expressions" + "uuid": "4e68e39a-e36c-4d2d-8714-eb6482e31ff5", + "slug": "while-loops", + "name": "While Loops" } ], "key_features": [ diff --git a/exercises/concept/elyses-destructured-enchantments/.docs/hints.md b/exercises/concept/elyses-destructured-enchantments/.docs/hints.md index bdad5257c2..c6c82baeac 100644 --- a/exercises/concept/elyses-destructured-enchantments/.docs/hints.md +++ b/exercises/concept/elyses-destructured-enchantments/.docs/hints.md @@ -2,32 +2,35 @@ ## 1. Get the first card -- [This article][destructuring_overview_resource] has a good overview of array destructuring. You can find an example of basic variable assignment in the 'Basic Array Destructuring' section. +- [This article][mdn-destructuring] has a good overview of array destructuring. You can find an example of basic variable assignment in the 'Basic Array Destructuring' section. ## 2. Get the second card - You can use placeholders to ignore one or more values in the array. -- You can find an example [here][ignoring_some_values_resource]. +- You can find an example [on MDN][mdn-destructuring-ignore-value]. -## 3. Swap the first two cards +## 3. Swap the two cards - It's possible to swap two values in a single destructuring expression. -- You can find an example [here][swapping_variables_resource]. +- You can find an example [on MDN][mdn-destructuring-swapping]. -## 4. Discard the top card +## 4. Shift three cards around -- There is a [built-in][rest_operator_docs] operator that can be used to collect the remaining values in an array into a single variable. -- You can find an example [here][rest_assignment_resource]. +- It's possible to change the position of three values in a single destructuring expression. +- This is the same as swapping two values, but then with three (or more). -## 5. Insert face cards +## 5. Pick named pile -- There is a [built-in][spread_operator_docs] operator that can be used to expand an array into a list. -- You can find a more detailed overview [here][spread_operator_overview]. +- Objects can be destructured just like arrays. +- You can find an example [on MDN][mdn-object-destructuring-basic-assignment]. -[destructuring_overview_resource]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Basic_variable_assignment -[ignoring_some_values_resource]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Ignoring_some_returned_values -[swapping_variables_resource]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Swapping_variables -[rest_operator_docs]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Rest_syntax_parameters -[rest_assignment_resource]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assigning_the_rest_of_an_array_to_a_variable -[spread_operator_docs]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax -[spread_operator_overview]: https://blog.alexdevero.com/javascript-spread-operator +## 6. Swap named piles + +- When a property is extracted from an object, it can be renamed using specific syntax. +- You can find an example [on MDN][mdn-object-destructuring-object-rename]. + +[mdn-destructuring]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Basic_variable_assignment +[mdn-destructuring-ignore-value]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Ignoring_some_returned_values +[mdn-destructuring-swapping]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Swapping_variables +[mdn-object-destructuring-basic-assignment]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring#basic_assignment +[mdn-object-destructuring-object-rename]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring#assigning_to_new_variable_names diff --git a/exercises/concept/elyses-destructured-enchantments/.docs/instructions.md b/exercises/concept/elyses-destructured-enchantments/.docs/instructions.md index 640a6ba7a2..57b66c3cc4 100644 --- a/exercises/concept/elyses-destructured-enchantments/.docs/instructions.md +++ b/exercises/concept/elyses-destructured-enchantments/.docs/instructions.md @@ -1,12 +1,29 @@ # Instructions -Elyse, magician-to-be, continues her training. She has a deck of cards she wants to manipulate. +Elyse, magician-to-be, continues her training. +She has a deck of cards she wants to manipulate. -To make things easier, she usually only starts with cards numbered 1 to 10, although some of the tricks may involve additional cards. +To make things easier, she usually only starts with cards numbered 2 to 10, although some of the tricks may involve additional (face) cards. + + +~~~~exercism/note +There are **many** ways to shuffle the cards around, but to keep up the illusion of magic, it is vital that *no single method is used*, e.g. Elyse doesn't use `splice`, `slice`, `shift`, `unshift`, `push`, `at`. +The array accessor `array[index]` and object accessor (`object[key]` and `object.key`) are also never to be used. +~~~~ + + +Want to help Elyse level up her magic? + + +~~~~exercism/advanced +Every function can be implemented using the parameters and a function body with a single `return expression`. +~~~~ + ## 1. Get the first card -Elyse will summon the first card in the deck without using the `array[index]` or `.shift()`. It's just like magic. +Elyse will summon the first card in the deck without using the `array[index]`, `.at(index)`, or `.shift()`. +It's just like magic. ```javascript const deck = [5, 9, 7, 1, 8]; @@ -17,7 +34,7 @@ getFirstCard(deck); ## 2. Get the second card -Elyse performs sleight of hand and summons the second card in the deck without using the `array[index]`. +Elyse performs sleight of hand and summons the second card in the deck without using the `array[index]` or `.shift()`. ```javascript const deck = [3, 2, 10, 6, 7]; @@ -26,35 +43,56 @@ getSecondCard(deck); // => 2 ``` -## 3. Swap the first two cards +## 3. Swap two cards + +Elyse will make the two cards of the deck switch places. +She doesn't need to call a single function. + +```javascript +const deck = [10, 7]; + +swapTwoCards(deck); +// => [7, 10] +``` + +## 4. Shift three cards around -Elyse will make the top two cards of the deck switch places. She doesn't need to call a single function. +In order to perform some more sleight of hand, Elyse takes three cards and quickly moves the top card to the back, making the middle card the first card and the old bottom card the middle card. +She doesn't need to call a single function. ```javascript -const deck = [10, 7, 3, 8, 5]; +const deck = [2, 6, 10]; -swapTopTwoCards(deck); -// => [7, 10, 3, 8, 5] +shiftThreeCardsAround(deck); +// => [6, 10, 2] ``` -## 4. Discard the top card +## 5. Pick the named pile -Elyse will separate the deck into two piles. The first pile will contain only the top card of the original deck, while the second pile will contain all the other cards. +Elyse will separate the deck into two piles. +She then asks the observer to pick one of the two piles, which we'll name `chosen`. +The `disregarded` pile is no longer relevant, which she makes disappear. +She doesn't need to call a single function. ```javascript -const deck = [2, 5, 4, 9, 3]; +const deck = [5, 4, 7, 10]; +const chosen = [5, 4]; +const disregarded = [7, 10]; -discardTopCard(deck); -// => [2, [5, 4, 9, 3]] +pickNamedPile({ chosen, disregarded }); +// => [5, 4] ``` -## 5. Insert face cards +## 5. Swap the picked pile -Elyse will insert a set of face cards (i.e. jack, queen, and king) into her deck such that they become the second, third, and fourth cards, respectively. +Unfortunately the observer keeps picking the "wrong" pile, but with some clever fast magic, Elyse renames the `chosen` pile to be `disregarded` and the `disregarded` pile to be the `chosen` pile. +She doesn't need to call a single function. ```javascript const deck = [5, 4, 7, 10]; +const chosen = [5, 4]; +const disregarded = [7, 10]; -insertFaceCards(deck); -// => [5, 'jack', 'queen', 'king', 4, 7, 10] +swapNamedPile({ chosen, disregarded }); +// => { chosen: [7, 10], disregarded: [5, 4] } ``` diff --git a/exercises/concept/elyses-destructured-enchantments/.docs/introduction.md b/exercises/concept/elyses-destructured-enchantments/.docs/introduction.md index b8ee42f346..24117e3b0b 100644 --- a/exercises/concept/elyses-destructured-enchantments/.docs/introduction.md +++ b/exercises/concept/elyses-destructured-enchantments/.docs/introduction.md @@ -14,31 +14,45 @@ neptune; // => 14 ``` -## Rest and spread +In short: -JavaScript has a built-in `...` operator that makes it easier to work with indefinite numbers of elements. Depending on the context, it's called either a _rest operator_ or _spread operator_. +- The syntax allows for naming _positioned_ elements in an array, as well as swapping variables using re-assignment. +- Destructuring syntax is available inside function parameters, and is available on any iterable. +- Leaving a position unnamed (by not writing _any_ variable name) silently ignores that position. -### Rest elements +## Object destructuring -When `...` appears on the left-hand side of an assignment, those three dots are known as the `rest` operator. The three dots together with a variable name is called a rest element. It collects zero or more values, and stores them into a single array. +In JavaScript, there is also destructuring syntax to extract properties from an object and assign them to distinct variables. -```javascript -const [a, b, ...everythingElse] = [0, 1, 1, 2, 3, 5, 8]; +In this example, weather symbols are extracted from the object `weather`: -everythingElse; -// => [1, 2, 3, 5, 8] +```javascript +const weather = { + sun: 'â˜€ī¸', + sun_behind_small_cloud: 'đŸŒ¤ī¸', + sun_behind_cloud: '⛅', + sun_behind_large_cloud: 'đŸŒĨī¸', + sun_behind_rain_cloud: 'đŸŒĻī¸', + cloud: 'â˜ī¸', + cloud_with_rain: 'đŸŒ§ī¸', + cloud_with_snow: 'đŸŒ¨ī¸', + cloud_with_lightning: 'đŸŒŠī¸', + cloud_with_lightning_and_rain: 'â›ˆī¸', +}; + +const { sun, cloud, cloud_with_lightning: thunder } = weather; + +sun; +// => 'â˜€ī¸' + +cloud; +// => 'â˜ī¸' + +thunder; +// => 'đŸŒŠī¸' ``` -Note that in JavaScript, unlike some other languages, a `rest` element cannot have a trailing comma. It _must_ be the last element in a destructuring assignment. - -### Spread elements - -When `...` appears on the right-hand side of an assignment, it's known as the `spread` operator. It expands an array into a list of elements. Unlike the rest element, it can appear anywhere in an array literal expression, and there can be more than one. +In short: -```javascript -const oneToFive = [1, 2, 3, 4, 5]; -const oneToTen = [...oneToFive, 6, 7, 8, 9, 10]; - -oneToTen; -// => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -``` +- The syntax allows for both extracting properties as well as extracting and renaming them. +- Destructuring syntax is available inside function parameters. diff --git a/exercises/concept/elyses-destructured-enchantments/.meta/design.md b/exercises/concept/elyses-destructured-enchantments/.meta/design.md index f2c20fea65..1db0602844 100644 --- a/exercises/concept/elyses-destructured-enchantments/.meta/design.md +++ b/exercises/concept/elyses-destructured-enchantments/.meta/design.md @@ -4,22 +4,19 @@ - Using destructuring to get the first item of an array - Using destructuring to get the second item of an array (skip hole) -- Using destructuring + rest elements to get the last item of an array - Using destructuring to get the first two items of an array -- Using destructuring + rest elements to get the head and tail of an array -- Using spread to turn an array into a list of parameters -- Using rest elements to turn a list of parameters into an array - Using destructuring to swap two values +- Using destructuring to extract properties from an object +- Using destructuring to extract properties from an object and rename them ## Out of scope -- Anything with objects - Default values ## Concepts - `array-destructuring` -- `rest-and-spread` +- `object-destructuring` ## Prerequisites diff --git a/exercises/concept/elyses-destructured-enchantments/.meta/exemplar.js b/exercises/concept/elyses-destructured-enchantments/.meta/exemplar.js index 748432b3a5..6d17a20971 100644 --- a/exercises/concept/elyses-destructured-enchantments/.meta/exemplar.js +++ b/exercises/concept/elyses-destructured-enchantments/.meta/exemplar.js @@ -24,39 +24,46 @@ export function getSecondCard([, second]) { } /** - * Switch the position of the first two cards in the given deck + * Switch the position of the two cards * - * @param {Card[]} deck + * @param {[Card, Card]} deck * - * @returns {Card[]} new deck with reordered cards + * @returns {[Card, Card]} new deck with the 2 cards swapped */ -export function swapTopTwoCards([a, b, ...rest]) { - return [b, a, ...rest]; +export function swapTwoCards([a, b]) { + return [b, a]; } /** - * Put the top card of the given deck into a separate discard pile + * Rotate (shift) the position of the three cards (by one place) * - * @param {Card[]} deck + * @param {[Card, Card, Card]} deck * - * @returns {[Card, Card[]]} the top card of the given - * deck and a new deck containing all the other cards + * @returns {[Card, Card, Card]} new deck with the 3 cards shifted by one position */ -export function discardTopCard([first, ...rest]) { - return [first, rest]; +export function shiftThreeCardsAround([a, b, c]) { + return [b, c, a]; } -/** @type Card[] **/ -const FACE_CARDS = ['jack', 'queen', 'king']; - /** - * Insert face cards into the given deck + * Grab the chosen pile from the available piles * - * @param {Card[]} deck + * @param {{ chosen: Card[], disregarded: Card[] }} piles + * + * @returns {Card[]} the pile named chosen + */ +export function pickNamedPile({ chosen }) { + return chosen; +} + +/** + * Swap the chosen pile for the disregarded pile and the disregarded pile for the chosen pile * - * @returns {Card[]} new deck where the second, - * third, and fourth cards are the face cards + * @param {{ chosen: Card[], disregarded: Card[] }} piles + * @returns {{ chosen: Card[], disregarded: Card[] }} new piles where the two piles are swapped */ -export function insertFaceCards([head, ...tail]) { - return [head, ...FACE_CARDS, ...tail]; +export function swapNamedPile({ chosen: disregarded, disregarded: chosen }) { + // đŸĒ„ Don't break the magic. + // Do NOT touch the next line or Elyse will accidentally reveal the trick. + return { chosen, disregarded }; } diff --git a/exercises/concept/elyses-destructured-enchantments/enchantments.js b/exercises/concept/elyses-destructured-enchantments/enchantments.js index 7483f0b4b8..563fea1eed 100644 --- a/exercises/concept/elyses-destructured-enchantments/enchantments.js +++ b/exercises/concept/elyses-destructured-enchantments/enchantments.js @@ -24,39 +24,46 @@ export function getSecondCard(deck) { } /** - * Switch the position of the first two cards in the given deck + * Switch the position of the two cards * - * @param {Card[]} deck + * @param {[Card, Card]} deck * - * @returns {Card[]} new deck with reordered cards + * @returns {[Card, Card]} new deck with the 2 cards swapped */ -export function swapTopTwoCards(deck) { +export function swapTwoCards(deck) { throw new Error('Implement the swapTopTwoCards function'); } /** - * Put the top card of the given deck into a separate discard pile + * Rotate (shift) the position of the three cards (by one place) * - * @param {Card[]} deck + * @param {[Card, Card, Card]} deck * - * @returns {[Card, Card[]]} the top card of the given - * deck and a new deck containing all the other cards + * @returns {[Card, Card, Card]} new deck with the 3 cards shifted by one position */ -export function discardTopCard(deck) { +export function shiftThreeCardsAround(deck) { throw new Error('Implement the discardTopCard function'); } -/** @type {Card[]} **/ -const FACE_CARDS = ['jack', 'queen', 'king']; - /** - * Insert face cards into the given deck + * Grab the chosen pile from the available piles * - * @param {Card[]} deck + * @param {{ chosen: Card[], disregarded: Card[] }} piles + * + * @returns {Card[]} the pile named chosen + */ +export function pickNamedPile(piles) { + throw new Error('Implement the pickNamedPile function'); +} + +/** + * Swap the chosen pile for the disregarded pile and the disregarded pile for the chosen pile * - * @returns {Card[]} new deck where the second, - * third, and fourth cards are the face cards + * @param {{ chosen: Card[], disregarded: Card[] }} piles + * @returns {{ chosen: Card[], disregarded: Card[] }} new piles where the two piles are swapped */ -export function insertFaceCards(deck) { - throw new Error('Implement the insertFaceCards function'); +export function swapNamedPile(piles) { + // đŸĒ„ Don't break the magic. + // Do NOT touch the next line or Elyse will accidentally reveal the trick. + return { chosen, disregarded }; } diff --git a/exercises/concept/elyses-destructured-enchantments/enchantments.spec.js b/exercises/concept/elyses-destructured-enchantments/enchantments.spec.js index 60a15cd4c0..11472da21b 100644 --- a/exercises/concept/elyses-destructured-enchantments/enchantments.spec.js +++ b/exercises/concept/elyses-destructured-enchantments/enchantments.spec.js @@ -1,10 +1,11 @@ import { describe, expect, test } from '@jest/globals'; import { - discardTopCard, getFirstCard, getSecondCard, - insertFaceCards, - swapTopTwoCards, + swapTwoCards, + shiftThreeCardsAround, + pickNamedPile, + swapNamedPile, } from './enchantments'; describe('getFirstCard', () => { @@ -27,7 +28,7 @@ describe('getSecondCard', () => { }); test('from a deck with many cards', () => { - expect(getSecondCard([2, 5, 1, 6])).toBe(5); + expect(getSecondCard([2, 5, 7, 6])).toBe(5); }); test('from an empty deck', () => { @@ -39,48 +40,77 @@ describe('getSecondCard', () => { }); }); -describe('swapTopTwoCards', () => { - test('in a deck with two cards', () => { - expect(swapTopTwoCards([3, 6])).toStrictEqual([6, 3]); +describe('swapTwoCards', () => { + test('swapping two numbered cards', () => { + expect(swapTwoCards([3, 6])).toStrictEqual([6, 3]); }); - test('in a deck with many cards', () => { - expect(swapTopTwoCards([10, 4, 3, 7, 8])).toStrictEqual([4, 10, 3, 7, 8]); + test('swapping a high card with a low card', () => { + expect(swapTwoCards([10, 2])).toStrictEqual([2, 10]); + }); + + test('swapping a face card with a low card', () => { + expect(swapTwoCards(['king', 3])).toStrictEqual([3, 'king']); }); }); -describe('discardTopCard', () => { - test('from a deck with one card', () => { - expect(discardTopCard([7])).toStrictEqual([7, []]); +describe('shiftThreeCardsAround', () => { + test('consecutive numbers', () => { + expect(shiftThreeCardsAround([6, 4, 5])).toStrictEqual([4, 5, 6]); }); - test('from a deck with many cards', () => { - expect(discardTopCard([9, 2, 10, 4])).toStrictEqual([9, [2, 10, 4]]); + test('drop the face card to the bottom', () => { + expect(shiftThreeCardsAround(['king', 5, 2])).toStrictEqual([5, 2, 'king']); }); }); -describe('insertFaceCards', () => { - test('into a deck with many cards', () => { - expect(insertFaceCards([3, 10, 7])).toStrictEqual([ - 3, - 'jack', - 'queen', - 'king', - 10, - 7, - ]); - }); - - test('into a deck with one card', () => { - expect(insertFaceCards([9])).toStrictEqual([9, 'jack', 'queen', 'king']); - }); - - test('into a deck with no cards', () => { - expect(insertFaceCards([])).toStrictEqual([ - undefined, - 'jack', - 'queen', - 'king', - ]); +describe('pickNamedPile', () => { + test('keeps the chosen pile', () => { + const chosen = [3, 'jack', 'queen', 'king', 10, 7]; + const disregarded = [4, 5, 6, 8, 9]; + const piles = { chosen, disregarded }; + + expect(pickNamedPile(piles)).toStrictEqual(chosen); + }); + + test('returns the actual pile without recreating it', () => { + const chosen = [3, 'jack', 'queen', 'king', 10, 7]; + const disregarded = [4, 5, 6, 8, 9]; + const piles = { chosen, disregarded }; + + const result = pickNamedPile(piles); + + chosen.push('joker'); + + expect(result).toStrictEqual(chosen); + }); +}); + +describe('swapNamedPile', () => { + test('renames the piles', () => { + const face_pile = [3, 'jack', 'queen', 'king', 10, 7]; + const numbers_pile = [4, 5, 6, 8, 9]; + const piles = { chosen: numbers_pile, disregarded: face_pile }; + + expect(swapNamedPile(piles)).toStrictEqual({ + chosen: face_pile, + disregarded: numbers_pile, + }); + }); + + test('returns the actual piles without recreating them', () => { + const face_pile = [3, 'jack', 'queen', 'king', 10, 7]; + const numbers_pile = [4, 5, 6, 8, 9]; + const piles = { chosen: numbers_pile, disregarded: face_pile }; + + const result = swapNamedPile(piles); + + face_pile.push('joker'); + numbers_pile.push(2); + + expect(result).toStrictEqual({ + chosen: face_pile, + disregarded: numbers_pile, + }); }); });