Podstawy Programowania Funkcyjnego #4 reduce

W tej serii uczymy się Podstaw Programowania Funkcyjnego. Wykorzystamy przy okazji wiedzę zdobytą w serii dotyczącej ES6. Poznaliśmy już funkcje filter i map, tym razem zajmiemy się tworzeniem jednej wartości z całej tablicy. Zaczynajmy!

# Czym jest Reduce?

W poprzednich postach poznaliśmy map i filter, czyli sposoby na przeszukiwanie i transformowanie tablic, tym razem jednak skorzystamy z bardziej rozbudowanego narzędzia, jakim jest reduce. Zwrócimy dzięki tej funkcji pojedynczą wartość na podstawie elementów tablicy. W prosty sposób będziemy mogli dodać wszystkie elementy tablicy, albo połączyć w string.

Reduce powinno przyjmować trzy argumenty: funkcję do wykonania na każdym elemencie, wartość początkową i tablicę na której będziemy pracować. Na początek zaimplementujemy sobie taką funkcję, gdzie callback to funkcja, start - wartość początkowa, a array to nasza tablica:

 
var reduce = function(callback, start, array) {
    var value = start;
    for (var i = 0; i < array.length; i = i + 1) {
        value = callback(value, array[i]);
    }
    return value;
};

Nasza funkcja przypisuje wartość początkową do zmiennej value, a następnie iteruje po kolejnych elementach tablicy wykonując dla każdego z nich podaną funkcję. Na końcu jako wynik zostaje zwrócona wartość value.

 
const add = (value, element) => value+element;

const concat = (value, element) => `${value} ${element}`;

Stworzyliśmy sobie dwie funkcje, z czego pierwsza z nich - add służy do dodawania dwóch elementów, a druga - concat słuzy do konkatenacji, czyli połączenia dwóch stringów w jeden. zapis w ES6 `${value} ${element}` jest równoważny value+' '+element.

Możemy teraz skorzystać z naszych funkcji, połączyć je i wykonać na tablicach:

const num = [1, 2, 3, 4, 5];
const WhoAmI = ["I", "am", "the", "one", "who", "knocks"]; 

reduce(add, 0, num); //15
reduce(concat,"",WhoAmI); //"I am the one who knocks"

Jednak w rzeczywistości nie musimy implementować funkcji reduce, ponieważ w JavaScript jest z nami już od ES5:

num.reduce(add); //15
WhoAmI.reduce(concat); //"I am the one who knocks"

# Wartość początkowa i inne argumenty

callback w Reduce może przyjmować cztery argumenty:

  • previousValue, czyli wartość zwróconą w ostatnim wywołaniu funkcji callback, lub wartość początkową jeśli taka została podana,
  • currentValue, czyli obecnie przetwarzany element tablicy,
  • index, index przetwarzanego elementu,
  • array, tablicę na której wykonujemy reduce.

Użyjmy poprzedniego przykładu z dodawaniem, jednak ustawmy wartość początkową na 100:

 
num.reduce((value, element) => value+element, 100); //115

# Tablica jako wartość zwracana

Poza stringiem czy liczbą, możemy zwrócić także tablice, wystarczy jako wartość początkową podać pustą tablicę, a następnie dodawać kolejne elementy funkcją push.

 
num.reduce((value, element) => {
  value.push(element*2);
  return value;
}, []); //[ 2, 4, 6, 8, 10 ]

Musimy pamiętać, że nasza funkcja wewnętrzna musi zwracać wartość, inaczej funkcja się nie wykona. W powyższym przykładzie korzystamy z tablicy liczb num, do pustej tablicy dodajemy kolejne elementy pomnożone przez 2. Na końcu otrzymujemy gotową tablicę. Oczywiście moglibyśmy wykonać to również funkcją map.

# Obiekt jako wartość zwracana

Poza pojedynczymi wartościami i tablicami, możemy także zbudować obiekt za pomocą reduce. W przykładzie pokaże wam, jak policzyć ilość wystąpień elementów w tablicy.

Tablica która znajduje się poniżej, to wystąpienia słów kluczowych w tekście opisującym fabułę serialu. Znajdziemy teraz najczęściej występujące słowa:

 
BreakingBad = ["chemistry", "crime", "teacher", "cancer", "student", "cancer", "DEA", "wife", "crystal", "wife", "crime"];
BreakingBad.reduce((obj, tag) => {
	obj[tag] = (obj[tag] || 0) + 1;
	return obj;
}, {}); /* { chemistry: 1,
  crime: 2,
  teacher: 1,
  cancer: 2,
  student: 1,
  DEA: 1,
  wife: 2,
  crystal: 1 } */

Callback powyższego kodu przyjmuje dwa argumenty - zmienną obj, której wartością początkową jest pusty obiekt, oraz aktualny element tablicy tag. Element, który aktualnie przetwarzamy staje się kluczem w tablicy i jeśli już taki istnieje to przypisana mu zostaje jego wartość powiększona o jeden. W przypadku jeśli dla danego elementu nie ma jeszcze miejsca w tablicy to zostaje stworzony z ustawioną wartością 0, powiększoną o jeden. Na końcu jak zwykle musimy pamiętać o zwróceniu zmiennej obj.

Jeśli nadal masz problem ze zrozumieniem kodu, spójrz na poniższą, bardziej rozpisaną wersję:

 
BreakingBad.reduce((obj, tag) => {
	if (!obj[tag]) {
		obj[tag] = 1;
	} else {
		obj[tag] = obj[tag] + 1;
	}
	return obj;
}, {});

Published: July 01 2017

blog comments powered by Disqus