diff --git a/05 Functions and Objects.md b/05 Functions and Objects.md index 39c9917..4faab1a 100644 --- a/05 Functions and Objects.md +++ b/05 Functions and Objects.md @@ -1,26 +1,13 @@ # Functions and Objects -Before you start this worksheet make sure you have the latest lab materials: - -```shell -$ git stash -$ git pull origin master -$ git stash pop -``` - -If the VI editor window pops open: - -1. press the Esc key. -2. type `:wq` and press the Enter key. - ## 1 Functions In JavaScript, as in most other languages, code can be divided in to modular blocks called functions. Once defined, these can be called from other code. Data can be passed in the form of parameters and functions can return data back to the calling code. Open the `maths.js` file. Notice that this contains several functions. Each is called directly under its definition. -### 1.1 Function Syntax +### 1.1 Function Declarations Lets start with a simple example. @@ -35,7 +22,7 @@ const biggest = largestNumber(5, 8) ``` 1. The function is declared using the `function` keyword and the function is given a name which must be a valid variable name. - a. If the name comprises more than one word these should be written using camel casing as shown above. + a. If the name comprises more than one word these should be written using camel casing as shown above. This is known as a **Function Declaration** 2. The function above takes two parameters, `a` and `b`. - These are variables with local scope (they can't ba accessed outside the function) - When the function is called, you need to pass two **values** which get assigned to the two parameters. @@ -45,102 +32,7 @@ const biggest = largestNumber(5, 8) a. If the numbers are not the same it returns the largest. b. If they are the same it returns `null`. -### 1.1.1 The Spread Operator - -If the data you want to pass to a function is stored in an `Array` (this is quite common), you could extract each value and assign to the function like this: - -```javascript -const nums = [5, 8] -const biggest2 = largestNumber(nums[0], nums[1]) -``` - -Because this is such a common task, there is a shortcut called the **spread operator**. Using this, the same task can be expressed like this. - -```javascript -const nums = [5, 8] -const biggest2 = largestNumber(...nums) -``` - -Notice the syntax of the _spread operator_. - -### 1.1.2 The Arguments Object - -When a function is declared it has a **signature** which defines the number of parameters it is expecting, for example in the `largestNumber()` function, the signature defines two arguments. - -```javascript -function largestNumber(a, b) { // this is the function signature. - // function body -} -``` - -What happens if you try to call this with the _wrong_ number of arguments? - -- If you supply too few arguments the remaining parameters are assigned a data type and value of `null`. -- If you supply too many arguments, the remaining ones are not assigned to the parameters, so where are they? - -Every JavaScript function has an object called `Arguments` which contains all the parameters passed to that function, even ones no assigned to the formal parameters. This provides a mechanism to access arguments that don't get assigned to parameters. Lets take a look at the `add()` function. - -```javascript -function add() { - let total = 0 - console.log(arguments) - console.log(arguments['1']) - for(const arg of arguments) { - total += arg - } - return total -} -``` - -As you can see, the function signature defines no parameters, but when we call it we pass 4 arguments. What happens to these and how can we access them? - -```javascript -const addNums = add(1, 2, 3, 4) -``` - -Inside the function we can access the `arguments` object. The `add()` function shows three ways to do this (we will be covering objects in detail in the next chapter): - -- display all the arguments (see below). -- access individual arguments by referencing a key. -- Use a `for...of` loop to iterate through the values. - -### 1.1.3 The Rest Parameter - -Whilst the `arguments` object provides a mechanism for accessing the function arguments, it returns an Object (the keys are `Strings`). It would be better if - -- the arguments could be accessed in an `Array`. -- it ignored arguments already assigned to parameters. - -ECMA6 introduced a special parameter called a _rest parameter_ which captures all the arguments that have not been assigned to parameters and stores them in an array. Look at the `add2()` function. - -```javascript -function add2(...values) { - let total = 0 - console.log(values) - console.log(values[1]) - for (let i=0; i num * num //TODO: add an exercise on function expressions. +1. Refactor the `remainder2` function expression to take advantage of the implicit return (you will need to reduce it to a single line of code). +2. Compare this to the original version: which is more _readable_? + ### 1.3 Callbacks +//TODO: need a replacemennt for the currency API (perhaps use screen scraping?) or books? + +NOT an API at this stage but reading/writing files? + Since JavaScript supports _first class functions_, we can use a function in any place where we could use a literal, object or variable. Open the `currency.js` script and look at line 17. As you can see the `request` object has a key called `get` that stores a function (we have already covered this). This takes two parameters: 1. A string representing the url to be accessed. @@ -250,36 +151,13 @@ Lets improve the currency exchange tool. You will need to refer to the API [docu 4. Use the [Number.prototype.toFixed()](https://goo.gl/DU4hvd) to truncate the number to 2 decimal places. 5. Finally, modify your program so that it throws an error if it doesn't recognise either of the currency codes. -## 1.4 Nested Callbacks - -Because the code to be run after a callback is run needs to be _inside_ the callback code it is very challenging to build a script that contains several long-running tasks you get into a situation where you nest callbacks inside callbacks (inside callbacks) which makes the code very difficult to write, debug and read and means its very difficult to split into separate functions, a situation commonly known as **Callback Hell**. - -Open the file `nestedCallbacks.js` which asks for a _base_ currency code then prints out all the exchange rates against other currencies. Notice that there are four functions defined, three of which include a callback. Our script is designed to capture user input using `stdin` (needing a callback), identify whether a currency code is valid (requiring a second callback) and then getting the currency conversion rates for the specified currency (requiring a third callback). - -1. Notice that the `checkValidCurrencyCode()` function is called by the callback for the `getInput()` function and the `getData()` function is called by the callback for the `checkValidCurrencyCode()` function. -2. Each callback takes two parameters as normal. The first contains the error (if any) and this needs to be handled in each callback. -3. The data from the first callback is needed when calling the third function so needs to be stored in an immutable variable (constant). -4. The fourth, and final, function does not have a callback. - -Callbacks are the simplest possible mechanism for asynchronous code in JavaScript. Unfortunately, raw callbacks sacrifice the control flow, exception handling, and function semantics familiar from synchronous code. - -### 1.4.1 Test Your Knowledge - -The callbacks are already nested 3 deep. To test your knowledge of deeply nested callbacks you are going to create a script that has 6 levels of nested callbacks! - -1. modify the script to ask for the currency to convert to and display only the one conversion rate. -2. instead of printing the exchange rate, ask for the amount to be converted and them return the equivalent in the chosen currency -3. use the [OpenExchangeRates](https://openexchangerates.org/api/currencies.json) API to display the full name of the chosen currency. - -Even though the script is still simple you are probably already getting in a tangle! Imagine a more complex script with conditions, it would quickly get out of hand and become practically impossible to debug. - ## 2 Objects Lets start by creating an manipulating objects using **object literals**. Open the `employee.js` file, read through it and see if you can work out what it does. Now run it to see if you were correct. ### 2.1 Creating Object Literals -The simplest way to create new objects is by creating an _object literal_ which is defining an object and storing it in a variable. You should open the `employee.js` file which contains the code. +The simplest way to create new objects is by creating an _object literal_ which is defining an object and storing it in a variable in a similar way to how we created function literals earlier in the lab. You should open the `employee.js` file which contains the code. ```javascript const employee = { @@ -520,90 +398,51 @@ There are a couple of important concepts here. 1. Extend the `Array` object by adding a function `toStr()` that takes an array and turns it into a string. You will need to use the [`Array.join()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join) function. --------- - - -#### 1.1.1 Callbacks - -The NodeJS language that is running on your server only has a single thread in an event loop (see diagram below). It is important to understand how this works and its benefits (and drawbacks). - -![NodeJS Event Loop](https://i.stack.imgur.com/BTm1H.png) - -Copyright medium.com - -All the incoming http requests from all connected users are placed in an event loop which means that they are processed one after another. Once the loop gets to the end of the connections it returns to the first one and continues. The main advantage of this is that the server does not have the overhead of creating new threads for each user however there is one obvious drawback, if one user's request takes time to complete everyone else is left waiting! - -To solve this problem, any task that is likely to be time-consuming is passed to an appropriate asynchronous interface (such as that used by a database or the filesystem). At this point a _callback_ is registered (this is normally a function). Once the time-consuming process has completed, the flow is returned to the event loop and the callback function is executed. +## 3 RESTful APIs and JSON Data -Let's see how this works in practice. Take a look at the `index.js` script once more: +//TODO: write this section...! -1. On line 12 we define a function called `hello`. This takes a `ctx` parameter which represents both the http request and http response. This is a pre-defined object. This is our _callback function_: - 1. Named functions are declared using the `function` keyword followed by the name of the function. - 2. Any parameters are enclosed in standard braces `()`, this can be empty if there are none. Multiple parameters are separated by commas. - 3. The body of the function is enclosed in curly braces `{}`. In this case the function contains a single line which sets the `body` property of our `ctx` parameter. This has the effect of sending this data back to the browser as the _response body_. -2. On line 16 we call a function that is stored in the `get` property of our new `router` object, this requires two parameters: - 1. The first parameter is the _route_ we want to handle (the end of the URL), `/` represents the _root url_. - 2. The second parameter is the function we want to run when this route is requested by the browser. -3. When the incoming http request is matched to an appropriate route the _callback function_ is called once the http request has been received. -4. The function is passed the `ctx` object so it can both access the request data and send a response to the web browser. +### 3.1 JSON Data -#### 1.1.2 Test Your Understanding +Show how objects can be turned into strings and saved. text data loaded and converted into a JavaScript object. -1. Create a function called `goodbye` that takes a `ctx` object and send the message `goodbye world` to the web browser. -2. Create a route for `/goodbye` and use the function you created in the previous step as its callback function. -3. Stop the server (ctrl+c) and restart. -4. Check it works. +#### 3.1.1 Test Your Understanding -#### 1.1.3 Anonymous Functions +### 3.2 RESTful APIs -In the previous examples we defined a named JavaScript function (it was assigned a name) and then passed this as a parameter to the `app.get()` function (as a _callback function_. This is a very common technique but going to the trouble to defined a named function and then passing the function by name as a function parameter (two steps) can be simplfied. Look at the two examples below. +Show how data can be retrieved from an API in JSON format. -Using a named _callback function_: +//TODO: use the OMDB API in this section -```javascript -function hello(ctx) { - ctx.body = 'Hello World' -} - -app.get('/', hello) -``` +OMDB key: 220d2590 -Using an _anonymous callback function_. Notice that we have inserted the entire function as the parameter without having to give it a name. Take a moment to make sense of the strange syntax. Each of the examples below is functionally identical: +First task is for students to get an OMDB API key and paste it into the provided script. -This first example uses the old style way to define an anonymous function using the `function` keyword: +### 3.3 Nested Callbacks -```javascript -app.get('/', function(ctx) { - ctx.body = 'Hello World' -} -``` +Use the same API and show that multiple steps cause nested callbacks and callback hell. -Modern JavaScript replaces the `function` keyword with an [arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions): +//TODO: use the OMDB API in this section. -```javascript -app.get('/', (ctx) => { - ctx.body = 'Hello World' -} -``` +Find films released in year X starring Y in genre Z from producer A, etc. country B. -If an arrow function has only one parameter, the braces are optional: +Because the code to be run after a callback is run needs to be _inside_ the callback code it is very challenging to build a script that contains several long-running tasks you get into a situation where you nest callbacks inside callbacks (inside callbacks) which makes the code very difficult to write, debug and read and means its very difficult to split into separate functions, a situation commonly known as **Callback Hell**. -```javascript -app.get('/', ctx => { - ctx.body = 'Hello World' -} -``` +Open the file `nestedCallbacks.js` which asks for a _base_ currency code then prints out all the exchange rates against other currencies. Notice that there are four functions defined, three of which include a callback. Our script is designed to capture user input using `stdin` (needing a callback), identify whether a currency code is valid (requiring a second callback) and then getting the currency conversion rates for the specified currency (requiring a third callback). -If the function body is only one line, the curly braces are also optional: +1. Notice that the `checkValidCurrencyCode()` function is called by the callback for the `getInput()` function and the `getData()` function is called by the callback for the `checkValidCurrencyCode()` function. +2. Each callback takes two parameters as normal. The first contains the error (if any) and this needs to be handled in each callback. +3. The data from the first callback is needed when calling the third function so needs to be stored in an immutable variable (constant). +4. The fourth, and final, function does not have a callback. -```javascript -app.get('/', ctx => ctx.body = 'Hello World' -``` +Callbacks are the simplest possible mechanism for asynchronous code in JavaScript. Unfortunately, raw callbacks sacrifice the control flow, exception handling, and function semantics familiar from synchronous code. -As you can see, there are a number of ways to make functions more concise however you should never sacrifice clarity for conciseness! +### 1.4.1 Test Your Knowledge -From this point onwards you will only be seeing (and using) anonymous callback functions. Take your time to ensure you fully understand the concepts and syntax before continuing. +The callbacks are already nested 3 deep. To test your knowledge of deeply nested callbacks you are going to create a script that has 6 levels of nested callbacks! -#### 1.1.4 Test Your Understanding +1. modify the script to ask for the currency to convert to and display only the one conversion rate. +2. instead of printing the exchange rate, ask for the amount to be converted and them return the equivalent in the chosen currency +3. use the [OpenExchangeRates](https://openexchangerates.org/api/currencies.json) API to display the full name of the chosen currency. -1. Replace the named `hello()` function with an anonymous function. +Even though the script is still simple you are probably already getting in a tangle! Imagine a more complex script with conditions, it would quickly get out of hand and become practically impossible to debug. diff --git a/exercises/05_functions_objects/countries.json b/exercises/05_functions_objects/countries.json new file mode 100644 index 0000000..3588034 --- /dev/null +++ b/exercises/05_functions_objects/countries.json @@ -0,0 +1,1002 @@ +[ + { + "country": "Name", + "code": "Code" + }, + { + "country": "Afghanistan", + "code": "AF" + }, + { + "country": "Åland Islands", + "code": "AX" + }, + { + "country": "Albania", + "code": "AL" + }, + { + "country": "Algeria", + "code": "DZ" + }, + { + "country": "American Samoa", + "code": "AS" + }, + { + "country": "Andorra", + "code": "AD" + }, + { + "country": "Angola", + "code": "AO" + }, + { + "country": "Anguilla", + "code": "AI" + }, + { + "country": "Antarctica", + "code": "AQ" + }, + { + "country": "Antigua and Barbuda", + "code": "AG" + }, + { + "country": "Argentina", + "code": "AR" + }, + { + "country": "Armenia", + "code": "AM" + }, + { + "country": "Aruba", + "code": "AW" + }, + { + "country": "Australia", + "code": "AU" + }, + { + "country": "Austria", + "code": "AT" + }, + { + "country": "Azerbaijan", + "code": "AZ" + }, + { + "country": "Bahamas", + "code": "BS" + }, + { + "country": "Bahrain", + "code": "BH" + }, + { + "country": "Bangladesh", + "code": "BD" + }, + { + "country": "Barbados", + "code": "BB" + }, + { + "country": "Belarus", + "code": "BY" + }, + { + "country": "Belgium", + "code": "BE" + }, + { + "country": "Belize", + "code": "BZ" + }, + { + "country": "Benin", + "code": "BJ" + }, + { + "country": "Bermuda", + "code": "BM" + }, + { + "country": "Bhutan", + "code": "BT" + }, + { + "country": "Bolivia, Plurinational State of", + "code": "BO" + }, + { + "country": "Bonaire, Sint Eustatius and Saba", + "code": "BQ" + }, + { + "country": "Bosnia and Herzegovina", + "code": "BA" + }, + { + "country": "Botswana", + "code": "BW" + }, + { + "country": "Bouvet Island", + "code": "BV" + }, + { + "country": "Brazil", + "code": "BR" + }, + { + "country": "British Indian Ocean Territory", + "code": "IO" + }, + { + "country": "Brunei Darussalam", + "code": "BN" + }, + { + "country": "Bulgaria", + "code": "BG" + }, + { + "country": "Burkina Faso", + "code": "BF" + }, + { + "country": "Burundi", + "code": "BI" + }, + { + "country": "Cambodia", + "code": "KH" + }, + { + "country": "Cameroon", + "code": "CM" + }, + { + "country": "Canada", + "code": "CA" + }, + { + "country": "Cape Verde", + "code": "CV" + }, + { + "country": "Cayman Islands", + "code": "KY" + }, + { + "country": "Central African Republic", + "code": "CF" + }, + { + "country": "Chad", + "code": "TD" + }, + { + "country": "Chile", + "code": "CL" + }, + { + "country": "China", + "code": "CN" + }, + { + "country": "Christmas Island", + "code": "CX" + }, + { + "country": "Cocos (Keeling) Islands", + "code": "CC" + }, + { + "country": "Colombia", + "code": "CO" + }, + { + "country": "Comoros", + "code": "KM" + }, + { + "country": "Congo", + "code": "CG" + }, + { + "country": "Congo, the Democratic Republic of the", + "code": "CD" + }, + { + "country": "Cook Islands", + "code": "CK" + }, + { + "country": "Costa Rica", + "code": "CR" + }, + { + "country": "Côte d'Ivoire", + "code": "CI" + }, + { + "country": "Croatia", + "code": "HR" + }, + { + "country": "Cuba", + "code": "CU" + }, + { + "country": "Curaçao", + "code": "CW" + }, + { + "country": "Cyprus", + "code": "CY" + }, + { + "country": "Czech Republic", + "code": "CZ" + }, + { + "country": "Denmark", + "code": "DK" + }, + { + "country": "Djibouti", + "code": "DJ" + }, + { + "country": "Dominica", + "code": "DM" + }, + { + "country": "Dominican Republic", + "code": "DO" + }, + { + "country": "Ecuador", + "code": "EC" + }, + { + "country": "Egypt", + "code": "EG" + }, + { + "country": "El Salvador", + "code": "SV" + }, + { + "country": "Equatorial Guinea", + "code": "GQ" + }, + { + "country": "Eritrea", + "code": "ER" + }, + { + "country": "Estonia", + "code": "EE" + }, + { + "country": "Ethiopia", + "code": "ET" + }, + { + "country": "Falkland Islands (Malvinas)", + "code": "FK" + }, + { + "country": "Faroe Islands", + "code": "FO" + }, + { + "country": "Fiji", + "code": "FJ" + }, + { + "country": "Finland", + "code": "FI" + }, + { + "country": "France", + "code": "FR" + }, + { + "country": "French Guiana", + "code": "GF" + }, + { + "country": "French Polynesia", + "code": "PF" + }, + { + "country": "French Southern Territories", + "code": "TF" + }, + { + "country": "Gabon", + "code": "GA" + }, + { + "country": "Gambia", + "code": "GM" + }, + { + "country": "Georgia", + "code": "GE" + }, + { + "country": "Germany", + "code": "DE" + }, + { + "country": "Ghana", + "code": "GH" + }, + { + "country": "Gibraltar", + "code": "GI" + }, + { + "country": "Greece", + "code": "GR" + }, + { + "country": "Greenland", + "code": "GL" + }, + { + "country": "Grenada", + "code": "GD" + }, + { + "country": "Guadeloupe", + "code": "GP" + }, + { + "country": "Guam", + "code": "GU" + }, + { + "country": "Guatemala", + "code": "GT" + }, + { + "country": "Guernsey", + "code": "GG" + }, + { + "country": "Guinea", + "code": "GN" + }, + { + "country": "Guinea-Bissau", + "code": "GW" + }, + { + "country": "Guyana", + "code": "GY" + }, + { + "country": "Haiti", + "code": "HT" + }, + { + "country": "Heard Island and McDonald Islands", + "code": "HM" + }, + { + "country": "Holy See (Vatican City State)", + "code": "VA" + }, + { + "country": "Honduras", + "code": "HN" + }, + { + "country": "Hong Kong", + "code": "HK" + }, + { + "country": "Hungary", + "code": "HU" + }, + { + "country": "Iceland", + "code": "IS" + }, + { + "country": "India", + "code": "IN" + }, + { + "country": "Indonesia", + "code": "ID" + }, + { + "country": "Iran, Islamic Republic of", + "code": "IR" + }, + { + "country": "Iraq", + "code": "IQ" + }, + { + "country": "Ireland", + "code": "IE" + }, + { + "country": "Isle of Man", + "code": "IM" + }, + { + "country": "Israel", + "code": "IL" + }, + { + "country": "Italy", + "code": "IT" + }, + { + "country": "Jamaica", + "code": "JM" + }, + { + "country": "Japan", + "code": "JP" + }, + { + "country": "Jersey", + "code": "JE" + }, + { + "country": "Jordan", + "code": "JO" + }, + { + "country": "Kazakhstan", + "code": "KZ" + }, + { + "country": "Kenya", + "code": "KE" + }, + { + "country": "Kiribati", + "code": "KI" + }, + { + "country": "Korea, Democratic People's Republic of", + "code": "KP" + }, + { + "country": "Korea, Republic of", + "code": "KR" + }, + { + "country": "Kuwait", + "code": "KW" + }, + { + "country": "Kyrgyzstan", + "code": "KG" + }, + { + "country": "Lao People's Democratic Republic", + "code": "LA" + }, + { + "country": "Latvia", + "code": "LV" + }, + { + "country": "Lebanon", + "code": "LB" + }, + { + "country": "Lesotho", + "code": "LS" + }, + { + "country": "Liberia", + "code": "LR" + }, + { + "country": "Libya", + "code": "LY" + }, + { + "country": "Liechtenstein", + "code": "LI" + }, + { + "country": "Lithuania", + "code": "LT" + }, + { + "country": "Luxembourg", + "code": "LU" + }, + { + "country": "Macao", + "code": "MO" + }, + { + "country": "Macedonia, the Former Yugoslav Republic of", + "code": "MK" + }, + { + "country": "Madagascar", + "code": "MG" + }, + { + "country": "Malawi", + "code": "MW" + }, + { + "country": "Malaysia", + "code": "MY" + }, + { + "country": "Maldives", + "code": "MV" + }, + { + "country": "Mali", + "code": "ML" + }, + { + "country": "Malta", + "code": "MT" + }, + { + "country": "Marshall Islands", + "code": "MH" + }, + { + "country": "Martinique", + "code": "MQ" + }, + { + "country": "Mauritania", + "code": "MR" + }, + { + "country": "Mauritius", + "code": "MU" + }, + { + "country": "Mayotte", + "code": "YT" + }, + { + "country": "Mexico", + "code": "MX" + }, + { + "country": "Micronesia, Federated States of", + "code": "FM" + }, + { + "country": "Moldova, Republic of", + "code": "MD" + }, + { + "country": "Monaco", + "code": "MC" + }, + { + "country": "Mongolia", + "code": "MN" + }, + { + "country": "Montenegro", + "code": "ME" + }, + { + "country": "Montserrat", + "code": "MS" + }, + { + "country": "Morocco", + "code": "MA" + }, + { + "country": "Mozambique", + "code": "MZ" + }, + { + "country": "Myanmar", + "code": "MM" + }, + { + "country": "Namibia", + "code": "NA" + }, + { + "country": "Nauru", + "code": "NR" + }, + { + "country": "Nepal", + "code": "NP" + }, + { + "country": "Netherlands", + "code": "NL" + }, + { + "country": "New Caledonia", + "code": "NC" + }, + { + "country": "New Zealand", + "code": "NZ" + }, + { + "country": "Nicaragua", + "code": "NI" + }, + { + "country": "Niger", + "code": "NE" + }, + { + "country": "Nigeria", + "code": "NG" + }, + { + "country": "Niue", + "code": "NU" + }, + { + "country": "Norfolk Island", + "code": "NF" + }, + { + "country": "Northern Mariana Islands", + "code": "MP" + }, + { + "country": "Norway", + "code": "NO" + }, + { + "country": "Oman", + "code": "OM" + }, + { + "country": "Pakistan", + "code": "PK" + }, + { + "country": "Palau", + "code": "PW" + }, + { + "country": "Palestine, State of", + "code": "PS" + }, + { + "country": "Panama", + "code": "PA" + }, + { + "country": "Papua New Guinea", + "code": "PG" + }, + { + "country": "Paraguay", + "code": "PY" + }, + { + "country": "Peru", + "code": "PE" + }, + { + "country": "Philippines", + "code": "PH" + }, + { + "country": "Pitcairn", + "code": "PN" + }, + { + "country": "Poland", + "code": "PL" + }, + { + "country": "Portugal", + "code": "PT" + }, + { + "country": "Puerto Rico", + "code": "PR" + }, + { + "country": "Qatar", + "code": "QA" + }, + { + "country": "Réunion", + "code": "RE" + }, + { + "country": "Romania", + "code": "RO" + }, + { + "country": "Russian Federation", + "code": "RU" + }, + { + "country": "Rwanda", + "code": "RW" + }, + { + "country": "Saint Barthélemy", + "code": "BL" + }, + { + "country": "Saint Helena, Ascension and Tristan da Cunha", + "code": "SH" + }, + { + "country": "Saint Kitts and Nevis", + "code": "KN" + }, + { + "country": "Saint Lucia", + "code": "LC" + }, + { + "country": "Saint Martin (French part)", + "code": "MF" + }, + { + "country": "Saint Pierre and Miquelon", + "code": "PM" + }, + { + "country": "Saint Vincent and the Grenadines", + "code": "VC" + }, + { + "country": "Samoa", + "code": "WS" + }, + { + "country": "San Marino", + "code": "SM" + }, + { + "country": "Sao Tome and Principe", + "code": "ST" + }, + { + "country": "Saudi Arabia", + "code": "SA" + }, + { + "country": "Senegal", + "code": "SN" + }, + { + "country": "Serbia", + "code": "RS" + }, + { + "country": "Seychelles", + "code": "SC" + }, + { + "country": "Sierra Leone", + "code": "SL" + }, + { + "country": "Singapore", + "code": "SG" + }, + { + "country": "Sint Maarten (Dutch part)", + "code": "SX" + }, + { + "country": "Slovakia", + "code": "SK" + }, + { + "country": "Slovenia", + "code": "SI" + }, + { + "country": "Solomon Islands", + "code": "SB" + }, + { + "country": "Somalia", + "code": "SO" + }, + { + "country": "South Africa", + "code": "ZA" + }, + { + "country": "South Georgia and the South Sandwich Islands", + "code": "GS" + }, + { + "country": "South Sudan", + "code": "SS" + }, + { + "country": "Spain", + "code": "ES" + }, + { + "country": "Sri Lanka", + "code": "LK" + }, + { + "country": "Sudan", + "code": "SD" + }, + { + "country": "Suriname", + "code": "SR" + }, + { + "country": "Svalbard and Jan Mayen", + "code": "SJ" + }, + { + "country": "Swaziland", + "code": "SZ" + }, + { + "country": "Sweden", + "code": "SE" + }, + { + "country": "Switzerland", + "code": "CH" + }, + { + "country": "Syrian Arab Republic", + "code": "SY" + }, + { + "country": "Taiwan, Province of China", + "code": "TW" + }, + { + "country": "Tajikistan", + "code": "TJ" + }, + { + "country": "Tanzania, United Republic of", + "code": "TZ" + }, + { + "country": "Thailand", + "code": "TH" + }, + { + "country": "Timor-Leste", + "code": "TL" + }, + { + "country": "Togo", + "code": "TG" + }, + { + "country": "Tokelau", + "code": "TK" + }, + { + "country": "Tonga", + "code": "TO" + }, + { + "country": "Trinidad and Tobago", + "code": "TT" + }, + { + "country": "Tunisia", + "code": "TN" + }, + { + "country": "Turkey", + "code": "TR" + }, + { + "country": "Turkmenistan", + "code": "TM" + }, + { + "country": "Turks and Caicos Islands", + "code": "TC" + }, + { + "country": "Tuvalu", + "code": "TV" + }, + { + "country": "Uganda", + "code": "UG" + }, + { + "country": "Ukraine", + "code": "UA" + }, + { + "country": "United Arab Emirates", + "code": "AE" + }, + { + "country": "United Kingdom", + "code": "GB" + }, + { + "country": "United States", + "code": "US" + }, + { + "country": "United States Minor Outlying Islands", + "code": "UM" + }, + { + "country": "Uruguay", + "code": "UY" + }, + { + "country": "Uzbekistan", + "code": "UZ" + }, + { + "country": "Vanuatu", + "code": "VU" + }, + { + "country": "Venezuela, Bolivarian Republic of", + "code": "VE" + }, + { + "country": "Viet Nam", + "code": "VN" + }, + { + "country": "Virgin Islands, British", + "code": "VG" + }, + { + "country": "Virgin Islands, U.S.", + "code": "VI" + }, + { + "country": "Wallis and Futuna", + "code": "WF" + }, + { + "country": "Western Sahara", + "code": "EH" + }, + { + "country": "Yemen", + "code": "YE" + }, + { + "country": "Zambia", + "code": "ZM" + }, + { + "country": "Zimbabwe", + "code": "ZW" + } +] \ No newline at end of file diff --git a/exercises/05_functions_objects/currency.json b/exercises/05_functions_objects/currency.json new file mode 100644 index 0000000..62026e0 --- /dev/null +++ b/exercises/05_functions_objects/currency.json @@ -0,0 +1,644 @@ +[ + { + "country": "European Union", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "USA", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Australia", + "currency": "Australian Dollar", + "code": "AUD", + "rate": 1.7282 + }, + { + "country": "Canada", + "currency": "Canadian Dollar", + "code": "CAD", + "rate": 1.6462 + }, + { + "country": "Aland Islands", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "American Samoa", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Andorra", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Australia", + "currency": "Australian Dollar", + "code": "AUD", + "rate": 1.7282 + }, + { + "country": "Austria", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Barbados", + "currency": "Barbadian Dollar", + "code": "BBD", + "rate": 2.3474 + }, + { + "country": "Belgium", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Brazil", + "currency": "Brazilian Real", + "code": "BRL", + "rate": 4.5301 + }, + { + "country": "Bulgaria", + "currency": "Bulgarian Lev", + "code": "BGN", + "rate": 2.0119 + }, + { + "country": "Canada", + "currency": "Canadian Dollar", + "code": "CAD", + "rate": 1.6462 + }, + { + "country": "China", + "currency": "Chinese Renminbi", + "code": "CNY", + "rate": 8.1426 + }, + { + "country": "Christmas Island", + "currency": "Australian Dollar", + "code": "AUD", + "rate": 1.7282 + }, + { + "country": "Cocos (Keeling) Islands", + "currency": "Australian Dollar", + "code": "AUD", + "rate": 1.7282 + }, + { + "country": "Cook Islands", + "currency": "New Zealand Dollar", + "code": "NZD", + "rate": 1.7508 + }, + { + "country": "Croatia", + "currency": "Croatian Kuna", + "code": "HRK", + "rate": 7.6781 + }, + { + "country": "Cyprus", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Czech Republic", + "currency": "Czech Koruna", + "code": "CZK", + "rate": 26.6556 + }, + { + "country": "Denmark", + "currency": "Danish Krone", + "code": "DKK", + "rate": 7.6552 + }, + { + "country": "Dominican Republic", + "currency": "Dominican Peso", + "code": "DOP", + "rate": 57.2267 + }, + { + "country": "Ecuador", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "El Salvador", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Estonia", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "European Union", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Faroe Islands", + "currency": "Danish Krone", + "code": "DKK", + "rate": 7.6552 + }, + { + "country": "Fiji", + "currency": "Fijian Dollar", + "code": "FJD", + "rate": 2.4674 + }, + { + "country": "Finland", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "France", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "French Guiana", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "French Southern Territories", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Germany", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Greece", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Greenland", + "currency": "Danish Krone", + "code": "DKK", + "rate": 7.6552 + }, + { + "country": "Guadeloupe", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Guam", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Haiti", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Holy See (Vatican City State)", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Hong Kong", + "currency": "Hong Kong Dollar", + "code": "HKD", + "rate": 9.537 + }, + { + "country": "Hungary", + "currency": "Hungarian Forint", + "code": "HUF", + "rate": 325.5257 + }, + { + "country": "Iceland", + "currency": "Icelandic Króna", + "code": "ISK", + "rate": 140.099 + }, + { + "country": "Indonesia", + "currency": "Indonesian Rupiah", + "code": "IDR", + "rate": 16 + }, + { + "country": "Ireland", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Israel", + "currency": "Israeli New Sheqel", + "code": "ILS", + "rate": 4.476 + }, + { + "country": "Italy", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Japan", + "currency": "Japanese Yen", + "code": "JPY", + "rate": 134.1945 + }, + { + "country": "Jordan", + "currency": "Jordanian Dinar", + "code": "JOD", + "rate": 0.8344 + }, + { + "country": "Kenya", + "currency": "Kenyan Shilling", + "code": "KES", + "rate": 119.2441 + }, + { + "country": "Kiribati", + "currency": "Australian Dollar", + "code": "AUD", + "rate": 1.7282 + }, + { + "country": "Korea, Republic of", + "currency": "South Korean Won", + "code": "KRW", + "rate": 1 + }, + { + "country": "Latvia", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Lesotho", + "currency": "South African Rand", + "code": "ZAR", + "rate": 17.4405 + }, + { + "country": "Liechtenstein", + "currency": "Swiss Franc", + "code": "CHF", + "rate": 1.2121 + }, + { + "country": "Lithuania", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Luxembourg", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Malaysia", + "currency": "Malaysian Ringgit", + "code": "MYR", + "rate": 4.912 + }, + { + "country": "Malta", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Marshall Islands", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Martinique", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Mauritius", + "currency": "Mauritian Rupee", + "code": "MUR", + "rate": 40.1008 + }, + { + "country": "Mayotte", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Mexico", + "currency": "Mexican Peso", + "code": "MXN", + "rate": 23.539 + }, + { + "country": "Micronesia, Federated States of", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Monaco", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Montenegro", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Namibia", + "currency": "South African Rand", + "code": "ZAR", + "rate": 17.4405 + }, + { + "country": "Nauru", + "currency": "Australian Dollar", + "code": "AUD", + "rate": 1.7282 + }, + { + "country": "Netherlands", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "New Zealand", + "currency": "New Zealand Dollar", + "code": "NZD", + "rate": 1.7508 + }, + { + "country": "Niue", + "currency": "New Zealand Dollar", + "code": "NZD", + "rate": 1.7508 + }, + { + "country": "Norfolk Island", + "currency": "Australian Dollar", + "code": "AUD", + "rate": 1.7282 + }, + { + "country": "Northern Mariana Islands", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Norway", + "currency": "Norwegian Krone", + "code": "NOK", + "rate": 10.5497 + }, + { + "country": "Palau", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Panama", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Philippines", + "currency": "Philippine Peso", + "code": "PHP", + "rate": 61.7703 + }, + { + "country": "Pitcairn", + "currency": "New Zealand Dollar", + "code": "NZD", + "rate": 1.7508 + }, + { + "country": "Poland", + "currency": "Polish Zloty", + "code": "PLN", + "rate": 4.3987 + }, + { + "country": "Portugal", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Puerto Rico", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Qatar", + "currency": "Qatari Riyal", + "code": "QAR", + "rate": 4.4062 + }, + { + "country": "Russian Federation", + "currency": "Russian Ruble", + "code": "RUB", + "rate": 80.3644 + }, + { + "country": "Saint Barthélemy", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Saint Martin (French part)", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "San Marino", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Saudi Arabia", + "currency": "Saudi Riyal", + "code": "SAR", + "rate": 4.5964 + }, + { + "country": "Singapore", + "currency": "Singapore Dollar", + "code": "SGD", + "rate": 1.6608 + }, + { + "country": "Slovakia", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Slovenia", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "South Africa", + "currency": "South African Rand", + "code": "ZAR", + "rate": 17.4405 + }, + { + "country": "Spain", + "currency": "Euro", + "code": "EUR", + "rate": 1.08 + }, + { + "country": "Sweden", + "currency": "Swedish Krona", + "code": "SEK", + "rate": 10.9409 + }, + { + "country": "Switzerland", + "currency": "Swiss Franc", + "code": "CHF", + "rate": 1.2121 + }, + { + "country": "Taiwan", + "currency": "New Taiwan Dollar", + "code": "TWD", + "rate": 36.3001 + }, + { + "country": "Thailand", + "currency": "Thai Baht", + "code": "THB", + "rate": 38.9959 + }, + { + "country": "Timor-Leste", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Tokelau", + "currency": "New Zealand Dollar", + "code": "NZD", + "rate": 1.7508 + }, + { + "country": "Turkey", + "currency": "Turkish New Lira", + "code": "TRY", + "rate": 6.2351 + }, + { + "country": "Turks and Caicos Islands", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Tuvalu", + "currency": "Australian Dollar", + "code": "AUD", + "rate": 1.7282 + }, + { + "country": "United Arab Emirates", + "currency": "UAE Dirham", + "code": "AED", + "rate": 4.4499 + }, + { + "country": "USA", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Vietnam", + "currency": "Vietnamese Dong", + "code": "VND", + "rate": 26 + }, + { + "country": "Virgin Islands, British", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + }, + { + "country": "Virgin Islands, U.S.", + "currency": "U.S. Dollar", + "code": "USD", + "rate": 1.2362 + } +] \ No newline at end of file diff --git a/exercises/05_functions_objects/currencyLogic.js b/exercises/05_functions_objects/currencyLogic.js new file mode 100644 index 0000000..5c2c4a0 --- /dev/null +++ b/exercises/05_functions_objects/currencyLogic.js @@ -0,0 +1,69 @@ +'use strict' + +const puppeteer = require('puppeteer') +const fs = require('fs') +const request = require('request') +const csv = require('fast-csv') + +const getRates = async query => { + const width = 1920 + const height = 926 + const browser = await puppeteer.launch({ headless: false}) + const page = await browser.newPage() + await page.setViewport({ width: width, height: height }) + await page.goto('https://www.travelex.co.uk/currency/exchange-rates', { waitUntil: 'domcontentloaded' }) + await page.waitFor(5000) + try { + await page.click('a#loadAll') + } catch (error) { + console.log('the loadAll button has already been clicked.') + } + console.log('ready to grab page content') + //const html = await page.content() + const dom = await page.evaluate(() => { + const elements = document.querySelectorAll('div.row') + console.log(`found ${elements.length} rows.`) + const hotels = [] + elements.forEach((element) => { + const quoteJson = {} + try { + //quoteJson.quote = element.innerText.replace(/ +/g, ',') + quoteJson.country = element.querySelector('span.col:first-child').innerText + //quoteJson.currencyStr = element.querySelector('span.col:nth-child(2)').innerText + quoteJson.currency = element.querySelector('span.col:nth-child(2)').innerText.split(' (')[0] + quoteJson.code = element.querySelector('span.col:nth-child(2)').innerText.split(' (')[1].replace(')', '') + quoteJson.rate = parseFloat(element.querySelector('span.col:nth-child(3)').innerText) + } catch (err) { + return new Error('oops') + } + hotels.push(quoteJson) + }) + return hotels + }) + await browser.close() + return dom +} + +const getCurrency = callback => getRates().then(data => callback(null, data)).catch(err => callback(err)) + +getCurrency( (err, data) => { + if(err) console.log('oops!') + console.log(`found ${data.length} CURRENCY codes`) + console.log(data.length) + fs.writeFileSync('currency.json', JSON.stringify(data, null, 2)) +}) + + +const getCountries = callback => { + const countryData = [] + csv.fromStream(request('https://datahub.io/core/country-list/r/0.csv')) + .on('data', (data) => countryData.push({country: data[0], code: data[1]})) + .on('end', () => callback(null, countryData)) +} + +getCountries( (err, data) => { + if(err) console.log('oops!') + //console.log(data) + console.log(`found ${data.length} COUNTRY codes`) + fs.writeFileSync('countries.json', JSON.stringify(data, null, 2)) +}) diff --git a/exercises/05_functions_objects/page.html b/exercises/05_functions_objects/page.html new file mode 100644 index 0000000..5b4f5f5 --- /dev/null +++ b/exercises/05_functions_objects/page.html @@ -0,0 +1,3304 @@ + + + + Currency Exchange - View Live Foreign Exchange Rates + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +TRAVELEX UK + + + + + + + + + + + + + + +
+
+
+
+
+ + + + + + + + + + + + + +
+
+
+ + + + +
+
+ + + + + + + +Buy Currency +
+
+ + + + + +
+
+
+ +
+ + + + + +
+
+
+

Currency exchange rates

+

View today’s foreign currency exchange rates below and sign up to our newsletter to stay up-to-date on the latest currency news, happy hours and offers.

+ + sign-up-now.png
+ +
+
+ + + + +
+
+
+
+ + + +
+ + + + +
+
+
+ + + + +
+
+ + +
+
+
+

Today’s exchange rates

We monitor market rates every day, to bring you great value on your travel money.

+

Whether you’re looking to convert your pounds to dollars, euros or any other currency, simply choose the currency you need below to see our rates of the day.

+
Today’s exchange rates
+
+
+ + + +
+
+
+
+

Find a currency

+ +
+
+
+ Currencies + Country + Currency + Sell Rate + More Info +
+
+ +
+ + Load all currencies + + +
+ + + +
+
+
+
+ + + + + + +
+

Read our great reviews on Trustpilot!

+
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Great Service

+

+ As ever Travelex has provided a great service. It has been both accurate and prompt. +

+ Susan Colebrook +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

So far so good

+

+ So far so good. As yet have not used on holiday but trust that all will be ok. +

+ Catherine Thomson +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

The whole experience was smooth and…

+

+ The whole experience was smooth and professional. And great exchange rates. :) +

+ Ruxandra Cordea +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Simple process

+

+ Simple process. Friendly staff +

+ AB +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Travelex - Ease of Use

+

+ Travelex is very good but one complaint: + +Why can't I leave all of my details for the next time? +

+ Colin McArthur +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

First time user

+

+ It was rad to use and seemingly safe. I have used them twice now. +

+ Me +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Great service with the best exchange…

+

+ Great service with the best exchange rates as always. Never let me down yet. +

+ Michael Mc +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

A good service but not fully secure

+

+ As usual the process was easy, and the delivery made the day indicated. +There are, however, two areas where improvement ... is desirable + +Delivery timing: an improvement would be a delivery slot of say 2 hours (or even before noon/after noon would be an improvement), as finding a day to be tied to home can be a challenge! + +Security: I had expected to prove ID on delivery - none was requested. Also my squiggle on a touch screen with my finger would never pass muster as an identifiable signature, and as no ID was requested there was no attempt to verify the 'signature'. A system that isn't really secure and open to failure by mistake or deliberate act! +

+ Eric Lowe +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

No unwelcome surprises on next month's statement!

+

+ Easy to top up card and track spend online. Reasonable exchange rate. Saves money in the long run as no transaction fees ... for purchases or withdrawing from cash machines when using currency selected - no unwelcome surprises on next month's statement! Helped me to control my non essential spending. No need to carry cash or multiple cards as welcome everywhere I went. Added security backed by MasterCard. Reusable - this is the second time I have used it and will definitely use again. +

+ Alondoner +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Holiday cash

+

+ Excellent speedy service. Will definitely use again. +

+ Susan Cotton +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

The best way to manage travel money no…

+

+ The best way to manage travel money no matter where you go. By far. +Even if you can't buy the currency direct you can get... decent exchange rates by using the card. +

+ N Portsmouth +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Great service

+

+ Great service. Helpful professional staff. +

+ K Harper +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Instant money transfer and could see…

+

+ Instant money transfer and could see transactions almost immediately. +

+ Alok Shah +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Everything was smooth on the website…

+

+ Everything was smooth on the website and next day delivery a big bonus. I've used the site several times and am very hap... py with it. +I have recommended it to several people. One little problem, the exchange rate! Oh well, can't have everything. +

+ Johnnyhupnorth +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Easy to find and helpful staff.

+

+ Was easy to find the correct desk. The gentleman that serverd us was very polite and chatty. Even though it was 5.30am +N... ot the best exchange rate but if you live left it a little late then this is the best option. Would probably use again . +

+ Shane +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Great Service

+

+ As ever Travelex has provided a great service. It has been both accurate and prompt. +

+ Susan Colebrook +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

So far so good

+

+ So far so good. As yet have not used on holiday but trust that all will be ok. +

+ Catherine Thomson +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

The whole experience was smooth and…

+

+ The whole experience was smooth and professional. And great exchange rates. :) +

+ Ruxandra Cordea +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Simple process

+

+ Simple process. Friendly staff +

+ AB +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Travelex - Ease of Use

+

+ Travelex is very good but one complaint: + +Why can't I leave all of my details for the next time? +

+ Colin McArthur +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

First time user

+

+ It was rad to use and seemingly safe. I have used them twice now. +

+ Me +
+ + + + + + + + + + + + + + + +
+
+ +
+
+
+

Travelex is rated 4 stars by Trustpilot based on over 34635 reviews

+
+
+ +
+
+ + + + + +
+
+
+
+
+

Travelex Wire: Low cost money transfer from the experts

+

Travelex Wire is our brand new money transfer service from Travelex directly, offering a low-cost way of sending money internationally.

+

With Travelex Wire, simply set up your money transfer online, with fees from as little as £3^.

Find out more
+
+
+
+

+ + How to calculate exchange rates + +

+
+

Exchange rates are influenced by banks and trading institutions and the volume of currency they are buying and selling at any given time. Currencies are traded (bought and sold) daily around the world.

+

One currency can be purchased by another currency through banking institutions or on the open market. The volumes of currencies traded are increased and decreased depending on the attractiveness of any particular currency, which depends on a multitude of factors such as political stability, economic strength, government debt and fiscal policy among others.

+

Government central banks also have the ability to set a currency at a constant price through a method called pegging, which essentially tethers the value of one currency to another. The value (or price) of a currency is determined by its traded volume. If a currency is competitively priced, traders will buy the currency, essentially driving up its value. If a currency is not competitively priced, traders may avoid buying, or even sell it, essentially driving down its value.

+ + + + + + + + +
+
+
+
+
+

+ + How to read exchange rates - currency jargon explained + +

+
+

Foreign exchange can be confusing, so to help break through the confusion, here are some common terms associated with currency:

+
    +
  • Buy rate This is the rate at which we buy foreign currency back from you into your local currency. For example, if you were returning from America, we would exchange your US dollars back into British pounds at the buy rate of the day.
  • +
+
    +
  • Commission – This is a common fee that foreign exchange providers charge for exchanging one currency with another.
  • +
+
    +
  • Cross rate – This is the rate we give to customers who want to exchange currencies that do not involve the local currency. For example, if you want to exchange Australian dollars into US dollars.
  • +
+
    +
  • Currency Pair - This the the relationship between two country's currencies. It is often denoted like this: GBP/USD, EUR/JAP, AUD/INR
  • +
+
    +
  • Holiday money rate or tourist rate – This is another term for a sell rate. 
  • +
+
    +
  • Sell rate This is the rate at which we sell foreign currency in exchange for local currency. For example, if you were heading to Europe, you would exchange British pounds for euros at the sell rate.
  • +
+
    +
  • Spot rate This is known more formally as the ‘interbank’ rate. It is the rate banks or large financial institutions charge each other when trading significant amounts of foreign currency. In the business, this is sometimes referred to as a ‘spot rate
  • +
+
    +
  • Spread – This is the difference between the buy and sell rates offered by a foreign exchange provider such as us. 
  • +
+ + + + + + + + +
+
+
+
+
+

+ + Exchange Rates FAQ's + +

+
+

Why do currency exchange rates fluctuate?

+

Currencies constantly move up and down against each other as financial markets change. These movements can be caused by supply and demand, as well as by political and economic events.

+

Why are tourist money exchange rates not the same as the market spot rate?

+

The market (or spot) exchange rate, is the rate at which banks exchange currencies. There are a lot of processes and people involved in providing currency into your hands. There is a cost to doing this, which means that the value of the currency is affected to cover all of said cost. 

+

At Travelex, we work to provide you with the best value on your foreign currency as possible. We are constantly striving to improve our systems and processes to make them more efficient, meaning that you get the best value for your travel money exchange rates from us.

+

Find out more on spot rates and tourist rates here.

+

+

Does it pay to shop around and compare rates?

+

There are a lot of foreign currency providers in the UK, offering you a range of products and services. With so much choice, it means that you can spend time to find the best exchange rate in the market. However, there is usually very little difference; it can be just a matter of pence. 

+ + + + + + + + +
+
+
+

Need some extra help with your currency?


Currency converter

See how much your mojito, poncho, or dinner in Paris will cost you in British pounds by using our simple currency converter.

Convert currency

Travel rate tracker

Not sure if it’s the right time to buy your currency? Let us do the hard work by monitoring the rates for you! We'll email you when your chosen currency hits the rate you need.

Track rates
+
+
+

*We compared the average cost of sending money abroad from the UK with Travelex Wire – in euros or US dollars across a range of values – against the average costs of sending equivalent sums abroad using the online services of leading money transfer providers in the UK. The price data used for the purpose of this analysis was obtained via online research between 1st June 2017 and 30th June 2017. Find out more here.

+ + + + + + + + +
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/exercises/05_functions_objects/quotes.js b/exercises/05_functions_objects/quotes.js index 73447b4..c917650 100644 --- a/exercises/05_functions_objects/quotes.js +++ b/exercises/05_functions_objects/quotes.js @@ -9,7 +9,7 @@ const search = async query => { const browser = await puppeteer.launch({ headless: true}) const page = await browser.newPage() await page.setViewport({ width: width, height: height }) - await page.goto(`https://www.brainyquote.com/search_results?q=${query}`, { waitUntil: 'domcontentloaded' }) + await page.goto(`https://www.brainyquote.com/search_results?q=${query}`, { waitUntil: 'networkidle0' }) await autoScroll(page) const dom = await page.evaluate(() => { const elements = document.querySelectorAll('div.m-brick') diff --git a/exercises/10_auth/jwt/authenticate.js b/exercises/10_auth/jwt/authenticate.js new file mode 100644 index 0000000..9a23ed4 --- /dev/null +++ b/exercises/10_auth/jwt/authenticate.js @@ -0,0 +1,20 @@ + +'use strict' + +const jwt = require('koa-jwt') + +module.exports = function(ctx) { + if (ctx.request.body.password === 'password') { + ctx.status = 200 + ctx.body = { + token: jwt.sign({ role: 'admin' }, 'A very secret key'), //Should be the same secret key as the one used is ./jwt.js + message: 'Successfully logged in!' + } + } else { + ctx.status = ctx.status = 401 + ctx.body = { + message: 'Authentication failed' + } + } + return ctx +} diff --git a/exercises/10_auth/jwt/customerService.js b/exercises/10_auth/jwt/customerService.js new file mode 100644 index 0000000..288fcad --- /dev/null +++ b/exercises/10_auth/jwt/customerService.js @@ -0,0 +1,33 @@ +'use strict' + +const customers = [ + { + 'id': 1, + 'first_name': 'John', + 'last_name': 'Smith', + 'date_of_birth': '1993-04-23T00:00:00.000Z' + }, + { + 'id': 2, + 'first_name': 'Justin', + 'last_name': 'Bieber', + 'date_of_birth': '1994-04-23T00:00:00.000Z' + } +] + +let maxId = 2 + +module.exports = { + getCustomers: function() { + return customers + }, + getCustomer: function(id) { + return customers.find(customer => customer.id === parseInt(id) || customer.id === id) + }, + postCustomer: function(customer) { + maxId++ + customer.id = maxId + customers.push(customer) + return this.getCustomer(maxId) + } +} diff --git a/exercises/10_auth/jwt/index.js b/exercises/10_auth/jwt/index.js new file mode 100644 index 0000000..943a2fd --- /dev/null +++ b/exercises/10_auth/jwt/index.js @@ -0,0 +1,46 @@ +#!/usr/bin/env node + +'use strict' + +// https://blog.theodo.fr/2016/11/securize-a-koa-api-with-a-jwt-token/ + +const Koa = require('koa') +const Router = require('koa-router') +//const koaBetterBody = require('koa-better-body') +const bodyParser = require('koa-bodyparser') + +const app = new Koa() +app.use(bodyParser()) +const router = new Router() + +const customerService = require('./customerService') +const jwt = require('./jwt') +const authenticate = require('./authenticate.js') + +//app.use(koaBetterBody({fields: 'body'})) + +router.post('/login', async ctx => { + authenticate(ctx) +}) + +router.get('/customer', jwt, async ctx => { + ctx.body = customerService.getCustomers() +}) + +router.get('/customer/:id', async ctx => { + if (customerService.getCustomer(this.params.id)) { + ctx.body = customerService.getCustomer(this.params.id) + } else { + this.status = 404 + ctx.body = {'error': 'There is no customer with that id'} + } +}) + +router.post('/customer', async ctx => { + this.body = customerService.postCustomer(this.request.body) +}) + +app.use(router.routes()) +//.use(router.allowedMethods()) + +app.listen(8080) diff --git a/exercises/10_auth/jwt/jwt.js b/exercises/10_auth/jwt/jwt.js new file mode 100644 index 0000000..cd514d3 --- /dev/null +++ b/exercises/10_auth/jwt/jwt.js @@ -0,0 +1,7 @@ +'use strict' + +const koaJwt = require('koa-jwt') + +module.exports = koaJwt({ + secret: 'A very secret key', // Should not be hardcoded +}) diff --git a/exercises/10_auth/jwt/package.json b/exercises/10_auth/jwt/package.json new file mode 100644 index 0000000..8a6dd8b --- /dev/null +++ b/exercises/10_auth/jwt/package.json @@ -0,0 +1,18 @@ +{ + "name": "jwt", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "koa": "^2.6.2", + "koa-better-body": "^3.0.4", + "koa-bodyparser": "^4.2.1", + "koa-jwt": "^3.5.1", + "koa-router": "^7.4.0" + } +}