8A Programming Arrays With Map, Filter and Reduce
8A Programming Arrays With Map, Filter and Reduce
Cosmin E. Oancea
[email protected]
October 2023
Data Structure
Solution: Arrays;
Summary
Passing Functions as Arguments to a Function
Passing functions as arguments to a function is a powerful tool!
f u n c t i o n u n i t Te s t 2 Ar g F u n ( f , a1 , a2 , e x p e c t e d r e s u l t , a r e E q u a l ) {
l e t a c t u a l r e s u l t = f ( a1 , a2 ) ;
i f ( areEqual ( a ct u a l r e s u l t , expected result ) ) {
return ” ” ;
} else {
l e t e r r o r s t r = ” \n\ t E r r o r : a p p l yi n g f u n c t i o n t o arguments : ( ”
+ a1 . t o S t r i n g ( ) + ” , ” + a2 . t o S t r i n g ( ) + ” ) ” +
” re su lt s in ” + a ct u a l r e s u l t +
” instead of ” + expected result ;
return e r r o r s t r ;
}
}
For example, it allows us to write one (generic) validation code for any function that
accepts two parameters:
f: the to-be-tested function;
a1 and a2 are its actual arguments;
expected result: the expected result of the call f(a1,a2)
areEqual: a function that compares whether the expected result equals the actual
result (e.g., because arrays cannot be compared directly with the == operator).
Map, Filter, Reduce
A lot can be accomplished by combining three array methods that
receive functions as arguments/parameters:
Summary
Mapping an Array
function plus1(a) {
return (a+1);
}
let x = [5, 6, 7, 8];
let y = x.map(plus1);
console.log(y); // [6, 7, 8, 9]
console.log(x); // [5, 6, 7, 8]
Mapping an Array
function plus1(a) {
return (a+1);
}
let x = [5, 6, 7, 8];
let y = x.map(plus1);
console.log(y); // [6, 7, 8, 9]
console.log(x); // [5, 6, 7, 8]
function plus1(a) {
return (a+1);
}
let x = [5, 6, 7, 8];
let y = x.map(plus1);
console.log(y); // [6, 7, 8, 9]
console.log(x); // [5, 6, 7, 8]
function plus1(a) {
return (a+1);
}
let x = [5, 6, 7, 8];
let y = x.map(plus1);
console.log(y); // [6, 7, 8, 9]
console.log(x); // [5, 6, 7, 8]
function plus1(a) {
return (a+1);
}
let x = [5, 6, 7, 8];
let y = x.map(plus1); // y = [6, 7, 8, 9]
Syntax for Unnamed (a.k.a., Lambda) Functions
function plus1(a) {
return (a+1);
}
let x = [5, 6, 7, 8];
let y = x.map(plus1); // y = [6, 7, 8, 9]
Summary
Filtering an Array
function even(a) {
return ( (a%2) == 0 );
}
let x = [5, 6, 7, 8, 9];
let y = x.filter(even); // y = [6, 8]
Summary
Reducing an Array
function plus(acc, elem) {
return (acc + elem);
}
let x = [5, 6, 7, 8];
let y = x.reduce(plus, 0);
// y = (((0 + 5) + 6) + 7) + 8 = 26
Reducing an Array
function plus(acc, elem) {
return (acc + elem);
}
let x = [5, 6, 7, 8];
let y = x.reduce(plus, 0);
// y = (((0 + 5) + 6) + 7) + 8 = 26
It can be written more concise as:
let y = x.reduce( (x,y) => x+y, 0 );
What should this compute?
function mul(acc, elem) {
// first arg (acc) is the accumulator
// second arg (elem) is an array element
return (acc * elem);
}
[5, 2, 8, 10].reduce(mul, 2);
Reducing an Array
function plus(acc, elem) {
return (acc + elem);
}
let x = [5, 6, 7, 8];
let y = x.reduce(plus, 0);
// y = (((0 + 5) + 6) + 7) + 8 = 26
It can be written more concise as:
let y = x.reduce( (x,y) => x+y, 0 );
What should this compute?
function mul(acc, elem) {
// first arg (acc) is the accumulator
// second arg (elem) is an array element
return (acc * elem);
}
[5, 2, 8, 10].reduce(mul, 2);
//(((2 * 5) * 2) * 8) * 10 = 1600
Semantics of Reduce: Important Intuition
Reduce lifts the behavior of a binary function that operates on
two arguments to operate instead on an arbitrary number of
arguments, i.e., on all elements of an array, for example:
one can write a small function that selects the smallest of its
two arguments → reduce with this function selects the
minimal element of an array.
Summary
Vertical Nesting: Doubling Each Element of a Matrix
Problem: from an input matrix (table of numbers), we want to create a new matrix that has
the same shape as the input matrix and whose elements are obtained by multiplying by
two the elements of the corresponding elements of the input matrix.
Implementation plan:
Vertical Nesting: Doubling Each Element of a Matrix
Problem: from an input matrix (table of numbers), we want to create a new matrix that has
the same shape as the input matrix and whose elements are obtained by multiplying by
two the elements of the corresponding elements of the input matrix.
Implementation plan:
(1) write a function that multiplies by two its (only) argument;
(2) write a function that multiplies by two each element of a vector,
by mapping the input vector with the function of step (1);
(3) write the function that solves our problem by mapping the input matrix with the
function of step (2).
(4) implement the target function directly in one line by using nested maps with
lambda functions.
Vertical Nesting: Doubling Each Element of a Matrix
Problem: from an input matrix (table of numbers), we want to create a new matrix that has
the same shape as the input matrix and whose elements are obtained by multiplying by
two the elements of the corresponding elements of the input matrix.
Implementation plan:
(1) write a function that multiplies by two its (only) argument;
(2) write a function that multiplies by two each element of a vector,
by mapping the input vector with the function of step (1);
(3) write the function that solves our problem by mapping the input matrix with the
function of step (2).
(4) implement the target function directly in one line by using nested maps with
lambda functions.
f u n c t i o n m u l 2 S ca l a r ( a ) { r e t u r n 2 * a ; }
f u n c t i o n m u l 2 Ve ct o r ( v c t ) { r e t u r n v c t . map ( m u l 2 S ca l a r ) ; }
f u n c t i o n m u l 2 M a t r i xN e s t ( mat ) {
r e t u r n mat . map ( row => row . map ( a => 2 * a ) ) ;
}
Filter Out the Odd Numbers of Each Row of A Matrix
Problem: we want to create a new matrix by filtering out all the odd elements (i.e., keep
the even ones) from each row of an input matrix.
Implementation plan:
Filter Out the Odd Numbers of Each Row of A Matrix
Problem: we want to create a new matrix by filtering out all the odd elements (i.e., keep
the even ones) from each row of an input matrix.
Implementation plan:
(1) write a function that returns true if the argument is an even number and false
otherwise;
(2) write a function that keeps only the even elements of a vector,
by applying filter with the operator defined at step (1);
(3) write a function that solves our problem by mapping the input matrix with the
function of step (2).
(4) implement the target function directly in one line by using a filter nested inside a
map, both being written with lambda functions.
Filter Out the Odd Numbers of Each Row of A Matrix
Problem: we want to create a new matrix by filtering out all the odd elements (i.e., keep
the even ones) from each row of an input matrix.
Implementation plan:
(1) write a function that returns true if the argument is an even number and false
otherwise;
(2) write a function that keeps only the even elements of a vector,
by applying filter with the operator defined at step (1);
(3) write a function that solves our problem by mapping the input matrix with the
function of step (2).
(4) implement the target function directly in one line by using a filter nested inside a
map, both being written with lambda functions.
f u n c t i o n i s E ve n ( a ) { r e t u r n ( ( a % 2 ) == 0 ) ; }
f u n c t i o n f i l t e r V e c t o r ( v c t ) { r e t u r n v c t . f i l t e r ( i s E ve n ) ; }
f u n c t i o n m a p F i l t e r N e s t ( mat ) {
r e t u r n mat . map ( row => row . f i l t e r ( a => ( a % 2 ) == 0 ) ) ;
}
Find the Maximal Element of Each Row of A Matrix
Problem: we want to create a new vector by selecting the maximal element of each row of
an input matrix (table of numbers).
Implementation plan:
Find the Maximal Element of Each Row of A Matrix
Problem: we want to create a new vector by selecting the maximal element of each row of
an input matrix (table of numbers).
Implementation plan:
(1) write a function that returns the larger of its two arguments;
(2) write a function that selects the maximal element of a vector,
by applying reduce with the operator defined at step (1) and what initial
accumulator?
(3) write a function that solves our problem by mapping the input matrix with the
function of step (2).
(4) implement the target function directly in one line by using a reduce nested inside a
map, both being written with lambda functions.
Find the Maximal Element of Each Row of A Matrix
Problem: we want to create a new vector by selecting the maximal element of each row of
an input matrix (table of numbers).
Implementation plan:
(1) write a function that returns the larger of its two arguments;
(2) write a function that selects the maximal element of a vector,
by applying reduce with the operator defined at step (1) and what initial
accumulator?
(3) write a function that solves our problem by mapping the input matrix with the
function of step (2).
(4) implement the target function directly in one line by using a reduce nested inside a
map, both being written with lambda functions.
f u n c t i o n maxVectorElm ( v c t ) { r e t u r n v c t . r e d u ce ( max , − I n f i n i t y ) ; }
f u n c t i o n mapReduceNest ( mat ) {
r e t u r n mat . map ( row => row . r e d u ce ( ( a , b ) => max ( a , b ) , − I n f i n i t y ) ) ;
}
Horizontal Nesting: String Processing Example
Problem: we want to process a string inp str that consists of words separated by
comma and/or spaces in the following way:
(1) replace the commas with empty spaces: this can be achieved with
let step1 str = inp str.replace(/[,]/g, " ")
(2) split the string into an array of words using space (” ”) as separator:
this can be achieved with step1 str.split(" ")
(3) removing the words that are empty strings or that represent numbers from the array
obtained at step (2)
(4) upper-casing all words of the array obtained at step (3) and adding an ”—” character
at the end of each.
(5) concatenating back all words into a string.
function processString ( i n p s t r ) {
l e t s t e p 1 s t r = i n p s t r . replace ( / [ , ] / g , ” ” ) ; / / r e p l a ce s ’ , ’ with ’ ’
l e t s t e p 2 a r r = s t e p 1 s t r . s p l i t ( ” ” ) ; / / p r o d u c e s an a r r a y o f wo r d s
l e t step3 arr = step2 arr . ???
l e t step4 arr = step3 arr . ???
let res str = step4 arr . ???
return r e s s t r ;
}
Horizontal Nesting: String Processing Example
Problem: we want to process a string inp str that consists of words separated by
comma and/or spaces in the following way:
(1) replace the commas with empty spaces: this can be achieved with let
step1 str = inp str.replace(/[,]/g, " ")
(2) split the string into an array of words using space (” ”) as separator: this can be
achieved with step1 str.split(" ")
(3) removing the words that are empty strings or that represent numbers from the array
obtained at step (2)
(4) upper-casing all words of the array obtained at step (3) and adding an ”—” character
at the end of each.
(5) concatenating back all words into a string that starts with ”—”.
function processString ( i n p s t r ) {
l e t s t e p 1 s t r = i n p s t r . replace ( / [ , ] / g , ” ” ) ;
let step2 arr = step1 str . split ( ” ” ) ;
l e t s t e p 3 a r r = s t e p 2 a r r . f i l t e r ( a => a ! = = ” ” && isNaN ( a ) )
l e t s t e p 4 a r r = s t e p 3 a r r . map ( word => word . toUpperCase ( ) + ” | ” )
let res str = s t e p 4 a r r . r e d u ce ( ( a , b ) −> a +b , ” | ” )
return r e s s t r ;
}
Can also be written in one very-long line (on whiteboard, please).
Summary