diff --git a/config.json b/config.json index 5d1c0d0309..4e55bb8e8d 100644 --- a/config.json +++ b/config.json @@ -116,10 +116,18 @@ "slug": "elyses-destructured-enchantments", "name": "Elyses Destructured Enchantments", "uuid": "d9b5cd13-2f2b-4034-a571-e66c847ed6f8", - "concepts": ["array-destructuring", "rest-and-spread"], + "concepts": ["array-destructuring"], "prerequisites": ["arrays", "functions", "objects"], "status": "beta" }, + { + "slug": "train-driver", + "name": "Train Driver", + "uuid": "6cef6712-cf1d-4b3e-9ace-1de3450b4285", + "concepts": ["rest-and-spread"], + "prerequisites": ["array-destructuring"], + "status": "beta" + }, { "slug": "elyses-looping-enchantments", "name": "Elyses Looping Enchantments", diff --git a/exercises/concept/train-driver/.docs/hints.md b/exercises/concept/train-driver/.docs/hints.md new file mode 100644 index 0000000000..a80b5c2af0 --- /dev/null +++ b/exercises/concept/train-driver/.docs/hints.md @@ -0,0 +1,32 @@ +# Hints + +## General + +- To extract multiple arguments in the function parameters so can you pack them with the `...`. +- To use rest and spread use the `...` operator. + +## 1. Create a list of all wagons + +- Multiple arguments in the function parameters can be packed with the [`...` (spread) syntax][spread-syntax]. operator. + +## 2. Move the first two elements to the end of the array + +- You can unpack a series of parameters using [a destructuring assignment (`...`)][destructuring-assignment]. + This lets you extract the first two elements of a `array` while keeping the rest intact. +- To add another `array` into an existing `array`, you can use the `...` operator to "spread" the `array`. + +## 3. Add missing values + +- Using unpacking with the rest operator(`...`), lets you extract the first two elements of a `array` while keeping the rest intact. +- To add another `array` into an existing `array`, you can use the `...` operator to "spread" the `array`. + +## 4. Extend routing information + +- To add another `object` into an existing `object`, you can use the `...` operator to "spread" the `object`. + +## 5. Separate arrival time from routing information + +- To extract a value from an `object` while keeping the rest intact, you can use the rest operator(`...`). + +[spread-syntax]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax +[destructuring-assignment]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment diff --git a/exercises/concept/train-driver/.docs/instructions.md b/exercises/concept/train-driver/.docs/instructions.md new file mode 100644 index 0000000000..6c466db264 --- /dev/null +++ b/exercises/concept/train-driver/.docs/instructions.md @@ -0,0 +1,96 @@ +# Instructions + +Your friend Linus is a train driver who drives cargo trains between cities. Although they are amazing at handling trains, they are not amazing at handling logistics or computers. They would like to enlist your programming help organizing train details and correcting mistakes in route data. + +```exercism/note +To practice, use the rest or spread operator to solve each of the tasks below. +``` + +## 1. Create a list of all wagons + +Your friend has been keeping track of each wagon identifier (ID), but they are never sure how many wagons the system is going to have to process at any given time. It would be much easier for the rest of the logistics program to have this data packaged into a unified `array`. + +Implement a function `getListOfWagons` that accepts an arbitrary number of wagon IDs which are the IDs of each wagon. +Each ID will be a positive integer. +The function should then return the given IDs as a single `array`. + +```javascript +getListOfWagons(1, 7, 12, 3, 14, 8, 5); +// => [1, 7, 12, 3, 14, 8, 3] +``` + +## 2. Move the first two elements to the end of the array + +At this point, you are starting to get a feel for the data and how it's used in the logistics program. The ID system always assigns the locomotive an ID of **1**, with the remainder of the wagons in the train assigned a randomly chosen ID greater than **1**. + +Your friend had to connect two new wagons to the train and forgot to update the system! Now, the first two wagons in the train `array` have to be moved to the end, or everything will be out of order. + +Linus would be really grateful to you for fixing their mistakes. + +Implement a function `fixListOfWagons` that accepts an array of the id of each wagon. +It `return` an `array` where the 2 first elements repositioned to the end of the `array` so that the locomotive can be in the front. + +```javascript +eachWagonsID = [2, 5, 1, 7, 4, 12, 6, 3, 13]; +fixListOfWagons(eachWagonsID); +// => [1, 7, 4, 12, 6, 3, 13, 2, 5] +``` + +## 3. Add missing values + +Uh-oh. some wagons seem to have gone missing. + +Fortunately, your friend just found another `array` which appears to contain the missing wagon IDs, and would like you to add them into the main wagon ID `array`. +All they can remember is that the missing values should be placed directly after the designated locomotive. + +Given this new information, write a function called `CorrectListOfWagons` that takes two arrays which have the IDs of each wagon as the arguments. +The wagon IDs of the second `array` should be added into the first `array` directly after the locomotive (ID 1). + +```javascript +eachWagonsID = [1, 5, 20, 7, 4, 8]; +missingWagons = [3, 17, 6, 15]; +correctListOfWagons(eachWagonsID, missingWagons); +// => [1, 3, 17, 6, 15, 5, 20, 7, 4, 8] +``` + +## 4. Extend routing information + +Now that all the wagon data is correct, your friend would like you to update the systems routing information. +Initial routing information has been constructed as an `object`, and you friend would like you to update it with the additions provided. +Every route requires slightly different information, so your friend would really prefer a generic solution. + +Implement a function extendRouteInformation that accepts two `objects`. +The first `object` contains which cities the train route moves between. + +The second `object` contains other routing details such as train speed or length. +The function should return a consolidated `object` with all routing information. + +```exercism/note +The variable `moreRouteInformation` can contain different properties. +``` + +```javascript +route = { from: 'Berlin', to: 'Hamburg' }; +moreRouteInformation = { length: '100', speed: '50' }; +extendRouteInformation(route, moreRouteInformation); +// => {from: "Berlin", to: "Hamburg", length: "100", speed: "50"} +``` + +## 5. Separate arrival time from routing information + +Your friend has noticed that they don't need the arrival time in the routing information. +Therefore your friend would like you to separate the arrival time from the routing information. + +Implement a function `separateArrivalTime` that accepts an object with the routing information. +The function should return an array there the first element of the array is the arrival time and the second element is an object with the routing information without arrival time. + +```javascript +routeInformation = { + from: 'Berlin', + to: 'Hamburg', + length: '100', + timeOfArrival: '10:10', +}; +separateTimeOfArrival(routeInformation); +// => ["10:10", {from: "Berlin", to: "Hamburg", length: "100"}] +``` diff --git a/exercises/concept/train-driver/.docs/introduction.md b/exercises/concept/train-driver/.docs/introduction.md new file mode 100644 index 0000000000..53dbcfa7be --- /dev/null +++ b/exercises/concept/train-driver/.docs/introduction.md @@ -0,0 +1,88 @@ +# Introduction + +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_. + +## Rest operator + +### Rest elements + +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. + +```javascript +const [a, b, ...everythingElse] = [0, 1, 1, 2, 3, 5, 8]; +a; +// => 0 +b; +// => 1 +everythingElse; +// => [1, 2, 3, 5, 8] +``` + +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. The example below throws a `SyntaxError`: + +```javascript +const [...items, last] = [2, 4, 8, 16] +``` + +### Rest properties + +Similarly to arrays, the rest operator can also be used to collect one or more object properties and store them in a single object. + +```javascript +const { street, ...address } = { + street: 'Platz der Republik 1', + postalCode: '11011', + city: 'Berlin', +}; +street; +// => 'Platz der Republik 1' +address; +// => {postalCode: '11011', city: 'Berlin'} +``` + +## Rest parameters + +When `...` appears in a function definition next to its last argument, that parameter is called a _rest parameter_. It allows the function to accept an indefinite number of arguments as an array. + +```javascript +function concat(...strings) { + return strings.join(' '); +} +concat('one'); +// => 'one' +concat('one', 'two', 'three'); +// => 'one two three' +``` + +## Spread + +### 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. + +```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] +const woow = ['A', ...oneToFive, 'B', 'C', 'D', 'E', ...oneToFive, 42]; +woow; +// => ["A", 1, 2, 3, 4, 5, "B", "C", "D", "E", 1, 2, 3, 4, 5, 42] +``` + +### Spread properties + +Similarly to arrays, the spread operator can also be used to copy properties from one object to another. + +```javascript +let address = { + postalCode: '11011', + city: 'Berlin', +}; +address = { ...address, country: 'Germany' }; +// => { +// postalCode: '11011', +// city: 'Berlin', +// country: 'Germany', +// } +``` diff --git a/exercises/concept/train-driver/.eslintrc b/exercises/concept/train-driver/.eslintrc new file mode 100644 index 0000000000..1d4446029c --- /dev/null +++ b/exercises/concept/train-driver/.eslintrc @@ -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" + } + ] +} diff --git a/exercises/concept/train-driver/.gitignore b/exercises/concept/train-driver/.gitignore new file mode 100644 index 0000000000..31c57dd53a --- /dev/null +++ b/exercises/concept/train-driver/.gitignore @@ -0,0 +1,5 @@ +/node_modules +/bin/configlet +/bin/configlet.exe +/pnpm-lock.yaml +/yarn.lock diff --git a/exercises/concept/train-driver/.meta/config.json b/exercises/concept/train-driver/.meta/config.json new file mode 100644 index 0000000000..b771267146 --- /dev/null +++ b/exercises/concept/train-driver/.meta/config.json @@ -0,0 +1,27 @@ +{ + "authors": [ + "meatball133" + ], + "contributors": [ + "bethanyg", + "junedev" + ], + "files": { + "solution": [ + "train-driver.js" + ], + "test": [ + "train-driver.spec.js" + ], + "exemplar": [ + ".meta/exemplar.js" + ] + }, + "blurb": "Professionalize using rest and spread operators.", + "custom": { + "version.tests.compatibility": "jest-27", + "flag.tests.task-per-describe": true, + "flag.tests.may-run-long": false, + "flag.tests.includes-optional": false + } +} diff --git a/exercises/concept/train-driver/.meta/design.md b/exercises/concept/train-driver/.meta/design.md new file mode 100644 index 0000000000..3d9bd201fd --- /dev/null +++ b/exercises/concept/train-driver/.meta/design.md @@ -0,0 +1,24 @@ +# Design + +## Learning objectives + +- Using spread to turn an array into a list of parameters +- Using rest elements to turn a list of parameters into an array +- Using spread to turn an extract value out of an object +- Using spread to combine objects +- Using rest to collect multiple parameters into an array + +## Out of scope + +- Default values + +## Concepts + +- `rest-and-spread` + +## Prerequisites + +- `arrays` are needed to understand array restructuring +- `functions` are needed as basis for rest parameters +- `objects` are needed for object spread etc. +- `array-destructuring` are needed to understand rest elements diff --git a/exercises/concept/train-driver/.meta/exemplar.js b/exercises/concept/train-driver/.meta/exemplar.js new file mode 100644 index 0000000000..8588849b45 --- /dev/null +++ b/exercises/concept/train-driver/.meta/exemplar.js @@ -0,0 +1,60 @@ +// @ts-check +// +// The line above enables type checking for this file. Various IDEs interpret +// the @ts-check directive. It will give you helpful autocompletion when +// implementing this exercise. + +/** + * Return each Wagons id in form of an array. + * + * @param {number[]} eachWagonsID + * @returns {number[]} each Wagons Wiegth + */ +export function getListOfWagons(...eachWagonsID) { + return eachWagonsID; +} + +/** + * Reorder the array of wagons by moving the first 2 wagons to the end of the array. + * + * @param {number[]} eachWagonsID + * @returns {number[]} reorderd list of wagons + */ +export function fixListOfWagons(eachWagonsID) { + const [first, second, ...rest] = eachWagonsID; + return [...rest, first, second]; +} + +/** + * Fixes the array of wagons by inserting an array of wagons after the first element in eachWagonsID. + * + * @param {number[]} eachWagonsID + * @param {number[]} missingWagons + * @returns {number[]} corrected list of wagons + */ +export function correctListOfWagons(eachWagonsID, missingWagons) { + const [first, ...rest] = eachWagonsID; + return [first, ...missingWagons, ...rest]; +} + +/** + * Extend route information by adding another object + * + * @param {Record} route + * @param {Record} moreRouteInformation + * @returns {Record} extended route information + */ +export function extendRouteInformation(route, moreRouteInformation) { + return { ...route, ...moreRouteInformation }; +} + +/** + * Separate arrival time from the route information object + * + * @param {Record} route + * @returns {[string, Record]} array with arrival time and object without arrival time + */ +export function separateTimeOfArrival(route) { + const { timeOfArrival, ...rest } = route; + return [timeOfArrival, rest]; +} diff --git a/exercises/concept/train-driver/.npmrc b/exercises/concept/train-driver/.npmrc new file mode 100644 index 0000000000..d26df800bb --- /dev/null +++ b/exercises/concept/train-driver/.npmrc @@ -0,0 +1 @@ +audit=false diff --git a/exercises/concept/train-driver/LICENSE b/exercises/concept/train-driver/LICENSE new file mode 100644 index 0000000000..90e73be03b --- /dev/null +++ b/exercises/concept/train-driver/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Exercism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/exercises/concept/train-driver/babel.config.js b/exercises/concept/train-driver/babel.config.js new file mode 100644 index 0000000000..b781d5a667 --- /dev/null +++ b/exercises/concept/train-driver/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + presets: ['@exercism/babel-preset-javascript'], + plugins: [], +}; diff --git a/exercises/concept/train-driver/package.json b/exercises/concept/train-driver/package.json new file mode 100644 index 0000000000..bc59ec8446 --- /dev/null +++ b/exercises/concept/train-driver/package.json @@ -0,0 +1,29 @@ +{ + "name": "@exercism/javascript-train-driver", + "description": "Exercism concept exercise on rest and spread operators", + "author": "Meatball133", + "private": true, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/exercism/javascript", + "directory": "exercises/concept/train-driver" + }, + "devDependencies": { + "@babel/core": "^7.22.10", + "@exercism/babel-preset-javascript": "^0.2.1", + "@exercism/eslint-config-javascript": "^0.6.0", + "@types/jest": "^29.5.4", + "@types/node": "^20.5.6", + "babel-jest": "^29.6.4", + "core-js": "~3.32.1", + "eslint": "^8.49.0", + "jest": "^29.6.3" + }, + "dependencies": {}, + "scripts": { + "test": "jest ./*", + "watch": "jest --watch ./*", + "lint": "eslint ." + } +} diff --git a/exercises/concept/train-driver/train-driver.js b/exercises/concept/train-driver/train-driver.js new file mode 100644 index 0000000000..d7c06e9c26 --- /dev/null +++ b/exercises/concept/train-driver/train-driver.js @@ -0,0 +1,57 @@ +// @ts-check +// +// The line above enables type checking for this file. Various IDEs interpret +// the @ts-check directive. It will give you helpful autocompletion when +// implementing this exercise. + +/** + * Return each Wagons id in form of an array. + * + * @param {number[]} eachWagonsID + * @returns {number[]} each Wagons Wiegth + */ +export function getListOfWagons(eachWagonsID) { + throw new Error('Please implement the getListOfWagons function'); +} + +/** + * Reorder the array of wagons by moving the first 2 wagons to the end of the array. + * + * @param {number[]} eachWagonsID + * @returns {number[]} reorderd list of wagons + */ +export function fixListOfWagons(eachWagonsID) { + throw new Error('Please implement the fixListOfWagons function'); +} + +/** + * Fixes the array of wagons by inserting an array of wagons after the first element in eachWagonsID. + * + * @param {number[]} eachWagonsID + * @param {number[]} missingWagons + * @returns {number[]} corrected list of wagons + */ +export function correctListOfWagons(eachWagonsID, missingWagons) { + throw new Error('Please implement the correctListOfWagons function'); +} + +/** + * Extend route information by adding another object + * + * @param {Record} route + * @param {Record} moreRouteInformation + * @returns {Record} extended route information + */ +export function extendRouteInformation(route, moreRouteInformation) { + throw new Error('Please implement the extendRouteInformation function'); +} + +/** + * Separate arrival time from the route information object + * + * @param {Record} route + * @returns {[string, Record]} array with arrival time and object without arrival time + */ +export function separateTimeOfArrival(route) { + throw new Error('Please implement the separateTimeOfArrival function'); +} diff --git a/exercises/concept/train-driver/train-driver.spec.js b/exercises/concept/train-driver/train-driver.spec.js new file mode 100644 index 0000000000..fca6d1cc5b --- /dev/null +++ b/exercises/concept/train-driver/train-driver.spec.js @@ -0,0 +1,175 @@ +import { + getListOfWagons, + fixListOfWagons, + correctListOfWagons, + extendRouteInformation, + separateTimeOfArrival, +} from './train-driver'; + +describe('getListOfWagons', () => { + test('return the correct array', () => { + expect(getListOfWagons(1, 5, 2, 7, 4)).toEqual([1, 5, 2, 7, 4]); + }); + + test('works for a few arrgument', () => { + expect(getListOfWagons(1, 5)).toEqual([1, 5]); + }); + + test('works for a one arrgument', () => { + expect(getListOfWagons(1)).toEqual([1]); + }); + + test('works for many argument', () => { + expect(getListOfWagons(1, 5, 6, 3, 9, 8, 4, 14, 24, 7)).toEqual([ + 1, 5, 6, 3, 9, 8, 4, 14, 24, 7, + ]); + }); +}); + +describe('fixListOfWagons', () => { + test('reorder the first 2 wagons to the end of the array', () => { + const eachWagonsID = [3, 7, 1, 14, 10, 4, 12, 6, 23, 17, 13, 20, 8, 19]; + const expected = [1, 14, 10, 4, 12, 6, 23, 17, 13, 20, 8, 19, 3, 7]; + expect(fixListOfWagons(eachWagonsID)).toEqual(expected); + }); + + test('works when only 3 wagons given', () => { + const eachWagonsID = [4, 2, 1]; + expect(fixListOfWagons(eachWagonsID)).toEqual([1, 4, 2]); + }); + + test('works for a few wagons', () => { + const eachWagonsID = [3, 4, 1, 5, 7, 9, 10]; + expect(fixListOfWagons(eachWagonsID)).toEqual([1, 5, 7, 9, 10, 3, 4]); + }); +}); + +describe('correctListOfWagons', () => { + test('returns a wagon wieght list with the inserted array of values', () => { + const eachWagonsID = [1, 6, 11, 15, 13, 14, 17, 22, 2, 16, 19, 21]; + const missingWagons = [8, 10, 5, 9, 3, 7, 20]; + const expected = [ + 1, 8, 10, 5, 9, 3, 7, 20, 6, 11, 15, 13, 14, 17, 22, 2, 16, 19, 21, + ]; + expect(correctListOfWagons(eachWagonsID, missingWagons)).toEqual(expected); + }); + + test('works for short arrays', () => { + const eachWagonsID = [1, 7, 15, 24]; + const missingWagons = [8, 6, 4]; + const expected = [1, 8, 6, 4, 7, 15, 24]; + expect(correctListOfWagons(eachWagonsID, missingWagons)).toEqual(expected); + }); + + test('works when missingWagons is longer', () => { + const eachWagonsID = [1, 7, 15, 24]; + const missingWagons = [8, 6, 4, 5, 9, 21, 2, 13]; + const expected = [1, 8, 6, 4, 5, 9, 21, 2, 13, 7, 15, 24]; + expect(correctListOfWagons(eachWagonsID, missingWagons)).toEqual(expected); + }); +}); + +describe('extendRouteInformation', () => { + test('correctly extend route information', () => { + const route = { from: 'Berlin', to: 'Hamburg' }; + const moreRouteInformation = { + timeOfArrival: '12:00', + precipitation: '10', + temperature: '5', + }; + const expected = { + from: 'Berlin', + to: 'Hamburg', + timeOfArrival: '12:00', + precipitation: '10', + temperature: '5', + }; + expect(extendRouteInformation(route, moreRouteInformation)).toEqual( + expected, + ); + }); + + test('works when not adding precipitation', () => { + const route = { from: 'Paris', to: 'London' }; + const moreRouteInformation = { timeOfArrival: '10:30', temperature: '20' }; + const expected = { + from: 'Paris', + to: 'London', + timeOfArrival: '10:30', + temperature: '20', + }; + expect(extendRouteInformation(route, moreRouteInformation)).toEqual( + expected, + ); + }); + + test('works when written in diffrent order', () => { + const route = { from: 'Gothenburg', to: 'Copenhagen' }; + const moreRouteInformation = { + precipitation: '1', + timeOfArrival: '21:20', + temperature: '-6', + }; + const expected = { + from: 'Gothenburg', + to: 'Copenhagen', + precipitation: '1', + timeOfArrival: '21:20', + temperature: '-6', + }; + expect(extendRouteInformation(route, moreRouteInformation)).toEqual( + expected, + ); + }); +}); + +describe('separateTimeOfArrival', () => { + test('seperate timeOfArrival from object', () => { + const route = { + from: 'Berlin', + to: 'Hamburg', + timeOfArrival: '12:00', + precipitation: '10', + temperature: '5', + }; + const expected = [ + '12:00', + { from: 'Berlin', to: 'Hamburg', precipitation: '10', temperature: '5' }, + ]; + expect(separateTimeOfArrival(route)).toEqual(expected); + }); + + test('seperate timeOfArrival with shorter object', () => { + const route = { + from: 'Paris', + to: 'London', + timeOfArrival: '10:30', + temperature: '20', + }; + const expected = [ + '10:30', + { from: 'Paris', to: 'London', temperature: '20' }, + ]; + expect(separateTimeOfArrival(route)).toEqual(expected); + }); + + test('seperate timeOfArrival from object', () => { + const route = { + from: 'Gothenburg', + to: 'Copenhagen', + precipitation: '1', + timeOfArrival: '21:20', + temperature: '-6', + }; + const expected = [ + '21:20', + { + from: 'Gothenburg', + to: 'Copenhagen', + precipitation: '1', + temperature: '-6', + }, + ]; + expect(separateTimeOfArrival(route)).toEqual(expected); + }); +});