diff --git a/async/async.solution.js b/async/async.solution.js new file mode 100644 index 0000000..4f43574 --- /dev/null +++ b/async/async.solution.js @@ -0,0 +1,63 @@ +const users = [ + {name: 'Peter'}, + {name: 'Paul'}, +]; +const otherUsers = [ + {name: 'Mary'} +]; + +/** + * 1. Write a function fetchUsers that accepts a callback funtion that + * will receive an array of user objects after 1 second. + * Print the user objects to the console + */ + +function fetchUsers(cb) { + setTimeout(() => { + cb(users); + }, 1000); +} + +fetchUsers(function (u) { + console.log('1. ', u); +}) + +/** + * 2. Modify the fetchUsers function to use promises. + * The function resolves after 1 second. + */ + + function promiseFetchUsers() { + return new Promise((resolve, reject) => { + setTimeout(() => {resolve(users)}, 1000) + }) + } + + promiseFetchUsers().then((u) => { + console.log('2. ', u); + }); + +/** + * 3. Use the fetchUsers function with async/await + */ +(async function () { + const u = await promiseFetchUsers(); + console.log('3. ', u); +})(); + +/** + * 4. Write a function fetchOtherUsers that returns a promise + * with other user objects, that resolves after 2 seconds + * + * a) Use Promise.race, to work with the fastest Promise + * b) Use Promise.all to combine the two results + */ + + function fetchOterUsers() { + return new Promise((resolve, reject) => { + setTimeout(() => {resolve(users)}, 2000) + }) + } + + Promise.race([promiseFetchUsers(), fetchOterUsers()]).then((u) => console.log('4a. ', u)); + Promise.all([promiseFetchUsers(), fetchOterUsers()]).then((u) => console.log('4b. ', u)); \ No newline at end of file diff --git a/basics/classes-and-objects/solution.js b/basics/classes-and-objects/solution.js new file mode 100644 index 0000000..17782aa --- /dev/null +++ b/basics/classes-and-objects/solution.js @@ -0,0 +1,113 @@ +// classes and objects + +/** + * 1. + */ +function User (firstName) {} +// ^ +// ____________| Note: the argument is actually not necessary for the code to run + + +/** + * 2. + */ +function User (firstName) { + this.firstName = firstName; +} + + +/** + * 3. + */ +function User (firstName, lastName) { + this.firstName = firstName; + this.lastName = lastName; +} +User.prototype.getFullName = function () { + return `${this.firstName} ${this.lastName}`; +}; + + +/** + * 4. + */ +const propertyValueArray = propertyArray.map(property => myFullnameUser[property]); + + +/** + * 5. + */ +const getFullnameFunc = myFullnameUser.getFullName.bind(myFullnameUser); +// ^ +// | this way the function will always get the +// ________________________________________________| myfullnameUser as a scope + + +/** + * 6. + */ +class User { + + constructor (firstName, lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + getFullName () { + return `${this.firstName} ${this.lastName}`; + } +} + + +/** + * 7. + */ +class User { + + // ... + + get fullName () { + return this.getFullName(); + } + + // ... +} + + +/** + * 8. + */ +class User { + + constructor(firstName, lastName, id) { + // ... + Object.defineProperty(this, 'id', { value: id, writable: false }); + } + + // ... +} + + +/** + * 9. + */ +class User { + + // ... + + static getClassDescription () { + return 'A User class.'; + } +} + + +/** + * 10. + */ +class AdminUser extends User { + + constructor (firstName, lastName, id) { + super(firstName, lastName, id); + this.admin = true; + } +} diff --git a/basics/loops/solution.js b/basics/loops/solution.js new file mode 100644 index 0000000..f8db2e5 --- /dev/null +++ b/basics/loops/solution.js @@ -0,0 +1,52 @@ +// loops + +/** + * 1. + */ +for (let i = 0, len = library.length; i < len; i++) { + const book = library[i]; + console.log(`${book.title} - ${book.read ? 'read' : 'not read'}`); +} + + +/** + * 2. + */ +library.forEach(book => console.log(`${book.title} - ${book.read ? 'read' : 'not read'}`)); + + +/** + * 3. + */ +for (const property in objForIn) { + + if (!objForIn.hasOwnProperty(property)) return; + + console.log(`${property} - ${objForIn[property]}`); +} + + +/** + * 4. + */ +// ... +const doubledNumbers = numbers.map(number => number * 2); +// ... + + +/** + * 5. + */ +// ... +const stringValues = mixedValues.filter(value => typeof value === 'string'); +// ... + + +/** + * 6. + */ +// ... +const sum = sumNumbers.reduce((total, number) => { + return total + number; +}, 0); +// ... diff --git a/basics/operators/solution.js b/basics/operators/solution.js new file mode 100644 index 0000000..0a11b99 --- /dev/null +++ b/basics/operators/solution.js @@ -0,0 +1,25 @@ +// strict equals, && vs. || + +/** + * 1. + */ +//----------- +// | change double '=' into triple '=' so that no conversion is done +// ⌄ +if (value === '5') { + console.log('hello text'); +} + + +/** + * 2. + */ +const result = ('a' && 'b') && 3 || 0 && (1 || 2 && 5); +// |__________| | | | +// | | | | +// 'b' AND 3 | | +// |_________| |________________| +// | | +// 3 OR [whatever] +// |_________________| +// 3 diff --git a/basics/rest-spread/solution.js b/basics/rest-spread/solution.js new file mode 100644 index 0000000..aef0381 --- /dev/null +++ b/basics/rest-spread/solution.js @@ -0,0 +1,22 @@ +// rest / spread + +/** + * 1. + */ + +// --------------------------| REST +// ⌄ +function makeItem (id, name, ...labels) { + + return { + id: id, + name: name, + labels: [ + 'item', + ...labels +// ^ +// ___| SPREAD + + ] + }; +} diff --git a/basics/value-vs-reference/solution.js b/basics/value-vs-reference/solution.js new file mode 100644 index 0000000..85af26f --- /dev/null +++ b/basics/value-vs-reference/solution.js @@ -0,0 +1,11 @@ +// value vs reference + +/** + * 1. + */ +function getObjectClone (origin) { + return JSON.parse(JSON.stringify(origin)); +// ^ +// | JSON.stringify transforms the entire object tree into a JSON string, +//_______| so when parsing the string, any references to the original object have been broken +} diff --git a/datatypes/datatypes.solution.js b/datatypes/datatypes.solution.js new file mode 100644 index 0000000..b8b707e --- /dev/null +++ b/datatypes/datatypes.solution.js @@ -0,0 +1,62 @@ +// data types + +/** + * 1. Given an array of values, find out how many falsy values are contained + */ +var falsyValues = ['a', 1, 0, '', [], {}, {a: 'b'}, false, {1: false}]; +var falsyCount = 0; +for (var i = 0; i < falsyValues; i++) { + if (!falsyValues[i]) { + falsyCount++; + } +} +console.log(falsyCount); + +console.log(falsyValues.reduce((prev, curr) => { + if (!curr) { + prev++; + } + return prev; +}, 0)) + +/** + * 2. There are pillars near the road. The distance between the pillars is the same and + * the width of the pillars is the same. Your function accepts three arguments: + * + * - number of pillars (>= 1) + * - distance between pillars (10 - 30 m) + * - width of the pillar (10 - 50 cm) + * + * Calculate the distance between the first and the last pillar in centimeters + * (without the width of the first and last pillar) + */ + +function pillars(numOfPillars, distance, width) { + const between = (numOfPillars - 1) * distance; + const pills = numOfPillars > 2 ? (numOfPillars - 2) * width : 0; + return between + pills; +} + +/** + * 3. An isogram is a word that has no repeating letters, consecutive or non-consecutive. + * Implement a function that determines whether a string that + * contains only letters is an isogram. Assume that empty string is an isogram. Ignore + * letter case. + */ + +function isIsogram(word) { + // your code goes here + var letters = {}; + for (var i = 0; i < word.length; i++) { + var char = word[i].toLowerCase(); + if (letters[char]) { + return false; + } + letters[char] = 1; + } + return true; +} + +console.log(isIsogram('Dermatoglyphics')); // true +console.log(isIsogram('aba')); // false +console.log(isIsogram('moOse')); // false diff --git a/dom/solution.js b/dom/solution.js new file mode 100644 index 0000000..ccec866 --- /dev/null +++ b/dom/solution.js @@ -0,0 +1,64 @@ +// DOM + +/** + * 1. + */ +const list = document.getElementById('list-with-id'); +['F', 'G'].forEach(text => { + const element = document.createElement('li'); + element.innerText = text; + list.appendChild(element); +}); + + +/** + * 2. + */ +list.removeChild(list.firstElementChild); +list.removeChild(list.firstElementChild); + + +/** + * 3. + */ +Array.from(list.getElementsByTagName('li')).forEach(element => { + + element.addEventListener('click', event => event.target.innerText += ' clicked'); +}); + + +/** + * 4. + */ +Array.from(list.getElementsByTagName('li')).forEach(element => { + + element.addEventListener('click', event => { + + // ... + + event.target.dispatchEvent(new CustomEvent('custom:my-click', { bubbles: true })); + }); +}); + + +/** + * 5. + */ +list.addEventListener('custom:my-click', () => { + + const item = document.createElement('li'); + item.innerText = 'item'; + list.appendChild(item); +}); + + +/** + * 6. + */ +list.addEventListener('click', event => { + + if (event.target.nodeName === 'LI') { + event.target.innerText += ' clicked'; + event.target.dispatchEvent(new CustomEvent('custom:my-click', { bubbles: true })); + } +}); diff --git a/functions/functions.solution.js b/functions/functions.solution.js new file mode 100644 index 0000000..046390f --- /dev/null +++ b/functions/functions.solution.js @@ -0,0 +1,78 @@ +// functions + +/** + * 1. Given a two-dimensional array of integers, return the flattened version of the arrray with all the integers sorted (ascending) order. + * + * Example: + * Geven [[3,2,1],[4,5,6],[],[9,7,8]], your function should return [1,2,3,4,5,6,7,8,9] + * + * Hint you can use Array.prototype.reduce and Array.prototype.sort for this + * Try to solve this task with arrow functions + */ + +function flatten(input) { + return input.reduce((prev, curr) => [...prev, ...curr], []).sort() +} +console.log(flatten([[3,2,1],[4,5,6],[],[9,7,8]])); + + /** + * 2. You will be given an array of objects representing developers. + * Your task is to return an array with objects where each object has a property greeting + * with the following string value: + * + * Hi , what do you like the most about ? + * + * + * + * const devs = [ + * {firstName: 'Peter', language: 'JavaScript', greeting: 'Hi Peter, whatdo you like the most about JavaScript'}, + * {firstName: 'Paul', language: 'JavaScript', greeting: 'Hi Paul, whatdo you like the most about TypeScript'}, + * {firstName: 'Mary', language: 'JavaScript', greeting: 'Hi Mary, whatdo you like the most about WebAssembly'}, + * ] + */ + +function greet(input) { + return input.map((user) => { + return { + ...user, + greeting: `Hi ${user.firstName}, what do you like the most about ${user.language}` + } + }) +} + +const devs = [ + {firstName: 'Peter', language: 'JavaScript'}, + {firstName: 'Paul', language: 'TypeScript'}, + {firstName: 'Mary', language: 'WebAssembly'}, +]; + +console.log(greet(devs)); +/*returns [ + * {firstName: 'Peter', language: 'JavaScript', greeting: 'Hi Peter, whatdo you like the most about JavaScript'}, + * {firstName: 'Paul', language: 'JavaScript', greeting: 'Hi Paul, whatdo you like the most about TypeScript'‚ }, + * {firstName: 'Mary', language: 'JavaScript', greeting: 'Hi Mary, whatdo you like the most about WebAssembly'}, + * ] + */ + + /** + * 3. Write a function that accepts an array and a callback function. The function returns two arrays. The first array + * contains all values for which the callback function returns the value true. The second array contains all the values + * for which the callback function returns false. + */ + + + +function split(arr, cb) { + return arr.reduce((prev, curr) => { + if (cb(curr)) { + prev[0].push(curr); + } else { + prev[1].push(curr); + } + return prev; + }, [[], []]); +} + +function isEven(x) {return x % 2 === 0} + +console.log(split([1,2,3,4], isEven)); // [[2,4],[1,3]] \ No newline at end of file diff --git a/patterns/factory/solution.js b/patterns/factory/solution.js new file mode 100644 index 0000000..295e85a --- /dev/null +++ b/patterns/factory/solution.js @@ -0,0 +1,22 @@ +// factory + +/** + * 1. + */ +function makeTicketMachine (attraction) { + + let number = 0; +// ^ +// |_ at this point, the variable is scoped to the attraction + + return function (passenger) { + + number++; +// ^ +// | and it can be accessed or changed from lower scopes +// |-------------------------------| +// | +// ⌄ + return `${attraction} Ticket #${number} for ${passenger}`; + } +} diff --git a/scope/scope.solution.js b/scope/scope.solution.js new file mode 100644 index 0000000..5d9ca16 --- /dev/null +++ b/scope/scope.solution.js @@ -0,0 +1,55 @@ +// scope + +// Hoisting + +/** + * 1. Why does the following code work? + * Fix the code, so it reflects what the js engine actually does + */ +function add(a, b) { + let result; // put it here + if (typeof a !== 'number' || typeof b !== 'number') { + console.log('Not a number'); + } + result = a + b; + + console.log('The result is: ', result); + var result; +} + +/** +* 2. Write a function createUser that returns a user object +* This user object has a function info, that prints the first and last name of the user and +* the age of the user. +* Use closeure scoping to permit editing of all three values +*/ + +function createUser(firstName, lastName, age) { + return { + info() { + console.log(firstName + ' ' + lastName + ' (' + age + ')'); + } + } +} + +const klaus = createUser('Klaus', 'Müller', 42); +klaus.info(); // Klaus Müller (42) +klaus.firstName = 'Klaus'; +klaus.info(); // Klaus Müller (42) + + +/** + * 3. Fix the following code + */ +function divide(a, b) { + let result; + try { + if (b === 0 ) { + throw new Error('Only Chuck Norris is able to divide by 0'); + } + result = a / b; + } catch (e) { + console.error(e); + } + console.log(result); +} \ No newline at end of file