IPW_IP 2425 1 A1 - isel-leic-ipw/2425i-IPW-LEIC33D GitHub Wiki
Bachelor in Informatics, Networks and Telecommunications Engineering
Winter Semester of 2023/2024 – 1st practical assignment
This first practical assignment has 2 parts, and it should be implemented and delivered individually by each student. The delivery dates are the following:
- Part1: 11/10/2024-23h59.
- Part2: 21/10/2024-23h59.
The delivery method is done in 2 steps:
- In Github Classroom, use this invitation link. Following this link, login with your github user account and select your student id to make the correspondence between them, so that a private Github repository is created. The assignment should be delivered in this repository, by creating a tag called
A1-P1
. If you need to make some amends to the delivery, just create another tag namedA1-P1.1
,A1-P1.2
, etc.
Implement the following JavaScript function and create tests that confirm the described behavior:
Implement the function validateArrayElements(arr, elementValidator)
that returns true
if all elements in the array pass the validation rules described in elementValidator
. If any element fails, it returns false
.
Arguments
-
arr
: an array of elements to be validated. -
elementValidator
: an object that validates each element in the array, having the following properties: -
validators
: an array of predicate functions that must be called with each element in the array. If any validator function returns false for an element, that element is invalid, and the function should return false.
Conditions
- Do not create any unnecessary functions (e.g. helpers).
- Create more object and validator usage examples, than the provided ones.
// Example1
const elementValidator = {
validators: [
(n) => typeof n === 'number' && n > 0, // checks if number is positive
(n) => n % 2 === 0 // checks if number is even
],
};
const arr1 = [2, 4, 6]; // all numbers are positive and even
const arr2 = [2, -4, 6]; // contains a negative number
const arr3 = [1, 3, 5]; // contains odd numbers
console.log(validateArrayElements(arr1, elementValidator)); // true
console.log(validateArrayElements(arr2, elementValidator)); // false
console.log(validateArrayElements(arr3, elementValidator)); // false
// Example2
const stringValidator = {
validators: [
(str) => typeof str === 'string', // checks if the element is a string
(str) => str.length > 3, // checks if the string has more than 3 characters
],
};
const strArr1 = ['hello', 'world', 'test']; // all strings, length > 3
const strArr2 = ['hi', 'ok', 'world']; // 'hi' and 'ok' have length <= 3
const strArr3 = [123, 'abc', 'def']; // contains non-string elements
console.log(validateArrayElements(strArr1, stringValidator)); // true
console.log(validateArrayElements(strArr2, stringValidator)); // false
console.log(validateArrayElements(strArr3, stringValidator)); // false
// Example 3
const mixedValidator = {
validators: [
(val) => typeof val === 'number', // checks if the element is a number
(val) => val >= 10 && val <= 100, // checks if the number is between 10 and 100
],
};
const mixedArr1 = [15, 25, 50, 100]; // all numbers between 10 and 100
const mixedArr2 = [5, 30, 50, 200]; // contains numbers outside the range
const mixedArr3 = [20, 'hello', 50]; // contains non-number elements
console.log(validateArrayElements(mixedArr1, mixedValidator)); // true
console.log(validateArrayElements(mixedArr2, mixedValidator)); // false
console.log(validateArrayElements(mixedArr3, mixedValidator)); // false
Implement the function copyValidArrayElements(arr, elementValidators)
that returns a new array with elements from arr
that pass the validation rules defined by elementValidators. Each element that does not pass the validation will not be included in the returned array. If no element is valid, an empty array is returned.
const elementValidators = [
{
validators: [s => typeof s === 'string' && s.length > 2, s => s[0] === 'a']
},
{
validators: [s => Number.isInteger(s)]
}
];
const arr1 = ["a"];
const arr2 = [123];
const arr3 = ["abc", 123];
console.log(copyValidArrayElements(arr1, elementValidators)); // []
console.log(copyValidArrayElements(arr2, elementValidators)); // [123]
console.log(copyValidArrayElements(arr3, elementValidators)); // ["abc", 123]
Arguments
-
arr
: an array of elements to be validated. -
elementValidators
: an array of validators, each containing an array of predicate functions that must be called with each element in the array. For each validator, if all validator functions returns true for an element, that element is valid and should not be included in the returned array.
Conditions
- Do not use any for/while loops or
Array#forEach
. - Do not create any unnecessary functions e.g. helpers.
- Create more object and validator usage examples, than the provided ones.
Hints
- The function must call
validateArrayElements
for each validator
Add a copyValidArrayElements(elementValidators)
method to Array type. This method implementation should call the copyValidArrayElements
function implemented in the previous exercise.
const elementValidators = [
{
validators: [s => typeof s === 'string' && s.length > 2, s => s[0] === 'a']
},
{
validators: [s => Number.isInteger(s)]
}
];
const arr1 = ["a"];
const arr2 = [123];
const arr3 = ["abc", 123];
console.log(arr1.copyValidArrayElements(elementValidators)); // []
console.log(arr2.copyValidArrayElements(elementValidators)); // [123]
console.log(arr3.copyValidArrayElements(elementValidators)); // ["abc", 123]
Add the groupBy(transformer)
method to the Array
prototype. This function returns an object where the keys are generated by applying the transformer
function to each element of the array, and the corresponding values are arrays containing all elements that, when passed to transformer
, produce the same key.
If two or more elements produce the same key, they should be grouped into the same array.
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(numbers.groupBy(n => n % 2 === 0 ? 'even' : 'odd'));
// Output:
// {
// odd: [1, 3, 5, 7, 9],
// even: [2, 4, 6, 8, 10]
// }
let people = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 30 },
{ name: "Charlie", age: 25 },
{ name: "David", age: 35 }
];
console.log(people.groupBy(person => person.age));
// Output:
// {
// 30: [{ name: "Alice", age: 30 }, { name: "Bob", age: 30 }],
// 25: [{ name: "Charlie", age: 25 }],
// 35: [{ name: "David", age: 35 }]
// }
Implement checkUsersValid(goodUsers)
function that returns a function. The returned function, takes a list of valid users in goodUsers
, and returns true
if all of the supplied users exist in the original list of users, or false
otherwise.
You only need to check that the user ids match.
const goodUsers = [
{ id: 1 },
{ id: 2 },
{ id: 3 }
]
// `checkUsersValid` is the function you'll define
const testAllValid = checkUsersValid(goodUsers)
testAllValid([
{ id: 2 },
{ id: 1 }
])
// => true
testAllValid([
{ id: 2 },
{ id: 4 },
{ id: 1 }
])
// => false
Arguments
-
goodUsers
: a list of valid users
Create a Memoization Function for Caching Results of Pure Functions.
In this exercise, you will create a memoize(funct)
function that caches the return values of pure functions based on their arguments, avoiding unnecessary recomputation when the same arguments are passed again.
A pure function is a function where the output is solely determined by its input and it has no side effects (i.e., it does not modify external state). Memoization optimizes such functions by remembering the results of previous invocations.
Create a memoize
function that takes another function as an argument and returns a new function. This new function will cache the results of the original function for specific arguments, so that if it is called again with the same arguments, it returns the cached result instead of recalculating it.
function add(a, b) {
console.log('Computing...');
return a + b;
}
const memoizedAdd = memoize(add);
console.log(memoizedAdd(2, 3)); // Computing... 5
console.log(memoizedAdd(2, 3)); // Cached result: 5
console.log(memoizedAdd(4, 1)); // Computing... 5
console.log(memoizedAdd(4, 1)); // Cached result: 5
Output
Computing...
5
5
Computing...
5
5
Extend your solution to handle functions with an arbitrary number of arguments.
function multiply(a, b, c) {
console.log('Multiplying...');
return a * b * c;
}
const memoizedMultiply = memoize(multiply);
console.log(memoizedMultiply(2, 3, 4)); // Multiplying... 24
console.log(memoizedMultiply(2, 3, 4)); // Cached result: 24
console.log(memoizedMultiply(1, 3, 4)); // Multiplying... 12
console.log(memoizedMultiply(1, 3, 4)); // Cached result: 12
Bonus Output:
Multiplying...
24
24
Multiplying...
12
12
This part requires that your code makes HTTP requests to the Football API.
To access this API, each student must request the API key that identifies the student application's requests. That key must be included in the header x-apisports-key
of each HTTP request. The description of the necessary steps to create the token are described here. Note that this is a free API, and therefore it has rate limits for your requests. Be sure you understand and comply to these limits.
This application reads team ids from a json file, with the following format:
{
"teams-ids" : [
211,
228,
212
]
}
and produces another JSON file containing each team id, name and stadium, as shown in following example:
{
"teams": [
{
"id": 211,
"name": "Benfica",
"stadium" : "Estádio do Sport Lisboa e Benfica (da Luz)"
},
{
"id": 228,
"name": "Sporting CP",
"stadium" : "Estádio José Alvalade"
},
{
"id": 212,
"name": "FC Porto",
"stadium" : "Estádio Do Dragão"
}
]
}
Implement two versions of the application:
- Using Promises explicitly
- Using the async/await style
REMARK: The students should consider and find solutions for the limitations of this free API, namely the Rate Limits in terms of the number of requests per day and per second.