Функціональне програмування.

Фронтенд розробка JavaScript. -> Сортування об'єктів.

Сортування об’єктів.

Припустимо, у нас є масив.

 var fruits = ['cherries', 'apples', 'bananas']

Функція сортування має вигляд.

arr.sort([функция-сравниватель])

Її можна спричинити без аргументів.

console.log(fruits.sort())

Загальний вигляд функції-порівняча

function cmp(a,b) {
    if ( a > b ) return 1;
    if ( a < b ) return -1;
    return 0; 
}

console.log(fruits.sort(cmp))

Припустимо, у нас є складний об’єкт.

var people = [
    {firstname: 'Dima', lastname: 'Ivanov'},
    {firstname: 'Vova', lastname: 'Putin'},
    {firstname: 'Anna', lastname: 'Karenina'}
]

Ми хочемо відсортувати його по полю firstname.

І тому визначимо таку функцію.

function cmp(a,b) {
    return ( a.firstеname > b.firstname )? 1: ( a.firstname < b.firstname )? -1: 0;
}

Удосконалимо функцію, передавши параметром значення поля, за яким хочемо відсортувати.

function sortBy(field) {
    return function(a,b) {
        return ( a[field] > b[field] )? 1: ( a[field] < b[field] )? -1: 0;
    }
}

Перевірка.

console.log(people.sort(sortBy('lastname')))
Фронтенд розробка JavaScript. -> Домашнє завдання. Чисті функції.

Домашнє завдання. Чисті функції.

Створити функцію вищого порядку forEachObject, яка має таку сигнатуру

forEachObject(object,function(property,object[property]))

Ця функція проходить за всіма властивостями об’єкта та застосовує функцію до кожної з властивостей згідно з сигнатурою вище.

При цьому необхідно брати властивості тільки цього об’єкта, не торкаючись успадкованих через prototype.

Чисті функції.

Чистими називають ті функції які працюють виключно з параметрами, що передаються в них.

Якщо ф-ция працює зі змінними, визначеними її межами, вона є чистої і має побічний ефект.

Припустимо, у нас є список.

var fruits = ['cherries', 'apples', 'bananas']

Визначимо функцію перебору елементів масиву.

function forEach(arr,fn){
    for(let i=0; i<=arr.length-1; i++){
        fn(arr[i]);
    }
}

Застосуємо її для виведення елементів у консоль.

forEach(fruits,(el)=> console.log(el))

Зробимо функцію, яка змінює елементи масиву.

function map(arr,fn){
    for(let i=0; i<=arr.length-1; i++){
        arr[i] = fn(arr[i]);
    }
}


map(fruits,(el)=> el.toUpperCase())

console.log(fruits);

Функція every.

Припустимо, є два масиви.

var myarr1 = [NaN,2,NaN,NaN];

var myarr2 = [NaN,NaN,NaN,NaN];

Необхідно перевірити чи є в масиві хоч один елемент, що не відповідає умові, яка укладена в функцію, і повернути true якщо всі відповідають або false якщо хоча б один немає.

const every = (arr,fn) => {
    let result = true;
    for(let i=0;i<arr.length;i++){
        result = result && fn(arr[i]);
    }
    return result;
};

У js є вбудована функція isNaN, яка перевіряє чи є значення числом.

console.log(every(myarr1,isNaN)); false

console.log(every(myarr2,isNaN)); true

Функція деяка.

Робить навпаки, повертає true якщо хоча б один відповідає і повертає true, інакше повертається false.

const some = (arr,fn) => {
    let result = false;
    for(let i=0;i<arr.length;i++){
        result = result || fn(arr[i]);
    }
    return result;
};

Необхідно відзначити неоптимальну роботу функції, коли вона у будь-якому разі пройде весь масив цілком, навіть якщо перший елемент поверне true і необхідності йти масивом далі немає.

За допомогою таких функцій можна писати програми у функціональному стилі, не використовуючи операторів умов (if else) та циклів (for).

Наприклад, щоб визначити чи присутня в масиві

var myarr1 = [NaN,2,NaN,'Dima'];

хоч одне значення ‘Dima’ можна так

console.log(some(myarr1,(e) => e === 'Dima')));

Сортування об’єктів.

Припустимо, у нас є масив.

 var fruits = ['cherries', 'apples', 'bananas']

Функція сортування має вигляд.

arr.sort([функція-порівняльник])

Її можна спричинити без аргументів.

console.log(fruits.sort())

Загальний вигляд функції-порівняча.

function cmp(a,b) {
    if ( a > b ) return 1;
    if ( a < b ) return -1;
    return 0; 
}

console.log(fruits.sort(cmp))

Припустимо, у нас є складний об’єкт.

var people = [
    {firstname: 'Dima', lastname: 'Ivanov'},
    {firstname: 'Vova', lastname: 'Putin'},
    {firstname: 'Anna', lastname: 'Karenina'}
]

Ми хочемо відсортувати його по полю firstname.

І тому визначимо таку функцію.

function cmp(a,b) {
    return ( a.firstеname > b.firstname )? 1: ( a.firstname < b.firstname )? -1: 0;
}

Удосконалимо функцію, передавши параметром значення поля, за яким хочемо відсортувати.

function sortBy(field) {
    return function(a,b) {
        return ( a[field] > b[field] )? 1: ( a[field] < b[field] )? -1: 0;
    }
}

Перевірка.

console.log(people.sort(sortBy('lastname'))))

Замикання.

Із замиканням пов’язана область пам’яті, в яку розміщуються змінні в момент повернення однієї функції з іншої.

Простіше кажучи замикання - це внутрішня функція зі змінними, які потрапляють до неї із зовнішньої.

function outer() {
    function inner() {
    }
}

Функція inner називається функцією - замиканням.

Уся сила цих функцій полягає у механізмі доступу до області видимості змінних усередині поточної функції.

Усього їх 3.

  1. Змінні, оголошені у власній області поточної функції.

  2. Змінні, оголошені у глобальному просторі (за межами функцій)

  3. Змінні, оголошені у зовнішній функції щодо поточної.

Варіант 1.

function outer() {
    function inner(){
        let a = 1;
        console.log(a);
    }
    inner();
}

Варіант 2.

var global = 3;
function outer() {
    function inner(){
        let a = 1;
        console.log(global);
    }
    inner();
}

Варіант 3.

function outer() {
    let outer = 'Outer';
    function inner(){
        let a = 1;
        console.log(outer);
    }
    inner();
}

При цьому зовнішня функція як би замикає простір видимості для внутрішньої функції-замикання та робить доступним змінні з неї.

Спробуємо повернути внутрішню функцію із зовнішньої, якій ще й передамо параметр.

var fn = (arg) => {
    let outer = 'Visible';
    let innerFn = () => {
        console.log(outer);
        console.log(arg);
    } 
    return innerFn;
}

Виклик буде наступним.

var closureFn = fn(5);
closureFn();

Спочатку ми присвоюємо змінній результат виконання fn, яка приймає аргумент і повертає функцію-замикання.

І ця функція-замикання матиме доступ як до аргументу arg, так і до outer.

Функція unary.

Допустимо ми хочемо перетворити рядки масиву на числа.

console.log(['1','2','3'].map((parseInt)))

висновок

[1, NaN, NaN]

Проблема в тому, що функція map має наступну сигнатуру

array.map(callback(item, index, array))

ссылка на перебирающие методы

А функция parseInt имеет сигнатуру

parseInt(string, radix);

де radix ця основа числа в математиці і коли map туди заштовхує порядковий номер елемента index - це змінює її роботу і не бажано

Тому створимо функцію unary, яка відсіє всі зайві параметри, що передаються map.

const unary = function(func) {
    if (func.length === 1){
        return func
    } else {
        return function(el)  {
            return func(el);
        }
    }
}

Або більш який варіант зі стрілочними функціями та тернарним оператором.

const unary = (func) => (func.length === 1)? func: (el) => func(el);

І тепер скористаємось.

console.log(['1','2','3'].map((unary(parseInt))));

Работа с массивами.

Функция map

Смысл - изменить массив функцией.

const map = (array, fn) => {
    let results = [];
    for(const value in array) {
        results.push(fn(value));
    }
    return results;
}

Застосування.

console.log(map(['1','2','3'],(el) => parseInt(el)));

Функція Filter.

Функція filter

Сенс - відсіяти елементи масиву, що не задовольняють умовам переданої функції (колбеку).

const filter = (arr,func) => {
    let result = [];
    for(const value of arr) {
        (func(value)) ? result.push(value): undefined;
    }
    return result;    
}

Відбираємо парні числа.

console.log(filter([1,2,3,4,5,6],(el) => el%2 === 0));