IPW_IP 2425 1 A1 - isel-leic-ipw/2425i-IPW-LEIC33D GitHub Wiki

Instituto Superior de Engenharia de Lisboa
Bachelor in Computer Science and Computer Engineering
Bachelor in Informatics, Networks and Telecommunications Engineering
Internet Programming/Introduction to Web Programming
Winter Semester of 2023/2024 – 1st practical assignment

Delivery

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.

Delivery method

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 named A1-P1.1, A1-P1.2, etc.

Part 1 - JavaScript Functions

Implement the following JavaScript function and create tests that confirm the described behavior:

1. validateArrayElements(arr, elementValidator)

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.

Examples

// 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

2. copyValidArrayElements(arr, elementValidators)

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.

Example

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

3. copyValidArrayElements(elementValidators)

Add a copyValidArrayElements(elementValidators) method to Array type. This method implementation should call the copyValidArrayElements function implemented in the previous exercise.

Example

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]

4. groupBy(transformer)

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.


Examples:

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 }]
// }

5. checkUsersValid(goodUsers)

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.

Example:

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

6. memoise(func)

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.

Task

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.

Example

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

Bonus

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

Part 2 - Node application

Foreword

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.

Application requirements

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:

  1. Using Promises explicitly
  2. 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.

⚠️ **GitHub.com Fallback** ⚠️