Mike Kasian
Mike Kasian

Reputation: 25

Write Higher Order Arrow Functions

I'm doing FreeCodeCamp tasks, and now I get stuck with Arrow Function Problem is : I need to sort an array(which "filter" function doing well - it sorts) but my map. function doesnt work. I get an error "((num > 0) && Number.isInteger(...)).map is not a function"

Thanks in advance

const realNumberArray = [4, 5.6, -9.8, 3.14, 42, 6, 8.34, -2];
const squareList = (arr) => {
  "use strict";
  const squaredIntegers = arr.filter((num) =>
   (num > 0 && Number.isInteger(num)).map((num) => Math.pow(num,2) )) 
  return squaredIntegers;
};

It should return an array the square of only the positive integers.

Upvotes: 0

Views: 281

Answers (5)

Dah Mike
Dah Mike

Reputation: 1

const realNumberArray = [4,5.6,-9.8,3.14,42,6,8.34,-2];
const squareList = (arr) => {
    const squaredIntegers = arr.filter(num => typeof num ==="number" && 
    isFinite(num) && Math.floor(num) === num && num>0).map(num => num ** 2)
    return squaredIntegers;
};
const squaredIntegers = squareList(realNumberArray);
console.log(squaredIntegers);

Upvotes: 0

Mulan
Mulan

Reputation: 135247

But you said higher-order arrow functions ^_^

const squareList = arr =>
  $ (arr)                        // beginning with arr,
    (filter (num => num > 0))    // filter
    (filter (Number.isInteger))  // filter some more
    (map (x => x ** 2))          // map
    ($)                          // extract result

const map = f => xs =>
  xs .map (x => f (x))           // Array.prototype.map
  
const filter = f => xs =>
  xs .filter (x => f (x))        // Array.prototype.filter

const $ = x =>      // beginning with x,
  f =>              // and a function, f,
    f === $         // exit condition
      ? x           // return x
      : $ (f (x))   // otherwise, transform x using f and recur

const realNumbers =
  [ 4, 5.6, -9.8, 3.14, 42, 6, 8.34, -2 ]
  
const result =
  squareList (realNumbers)

console.log(result)
// [ 16, 1764, 36 ]

Functions like $ allow us to think about our problem in different ways. Above we see squareList take an array and pass it down the "pipe" vertically. Intuitively, it's nice to think about the problem as sequence of arr->filter->filter->map but this is a wasteful computation. As others have pointed out, it can be done with a single reduce. Wouldn't it be nice if we could have our cake and eat it too?

Functional programming is all about programming with functions. This doesn't mean we have to forfeit writing programs in a particular way. Simply write functions to isolate complexity and do the hard work for you.

Below, squareList is exactly the same as above, but this time we change the program's behaviour dramatically by refactoring $. Instead of running filter->filter->map in sequence, this version builds up a pending computation and runs a single reduce when the value is popped out. A popular name for this technique is transducers; here I've encoded them using arrow functions.

Higher-order arrow functions for days ^_^

const squareList = arr =>
  $ (arr)                       // beginning with arr,
    (filter (num => num > 0))   // add filter transducer
    (filter (Number.isInteger)) // add another filter transducer
    (map (x => x ** 2))         // add map transducer
    ($)                         // run transducer

const map = f =>                   // map transducer
  k => (r, x) => k (r, f (x))
  
const filter = f =>                // filter transducer
  k => (r, x) => f (x) ? k (r, x) : r

const $ = (x, t = identity) =>     // beginning with x and base transducer,
  f =>                             // and a function, f,
    f === $                        // exit condition
      ? x .reduce (t (push), [])   // run transducer; single reduce
      : $ (x, comp (t, f))         // otherwise, recur with new transducer

const identity = x =>
  x

const push = (r, x) =>
  ( r .push (x)
  , r
  )

const comp = (f, g) =>
  x => f (g (x))

const realNumbers =
  [ 4, 5.6, -9.8, 3.14, 42, 6, 8.34, -2 ]
  
const result =
  squareList (realNumbers)

console.log(result)
// [ 16, 1764, 36 ]

Above $ only iterates over the input array once, compared to filter->filter->map which iterates over the input array three (3) times. This is a big performance gain in scenarios where arr is significantly large.

$ could be anything – and because it's a function, it gives us a domain we can control and make meaningful changes; maybe we make some performance optimisations; maybe it could throw a TypeError if an invalid argument is given; maybe we call it pipe or call it trans or whatever. Don't get stuck thinking functional programming begins and ends at Array.prototype.map, Array.prototype.filter, Array.prototype.reduce et al. You imagine the magic wand and you create it ^_^

Upvotes: 0

Joven28
Joven28

Reputation: 769

You can do this using only reduce function.

const list = [4, 5.6, -9.8, 3.14, 42, 6, 8.34, -2];
const result = list.reduce((acc, val) => {
  if (Number.isInteger(val) && val > 0) {acc.push(val*val)};
  return acc;
}, []);
console.log(result);

Upvotes: 0

Praneeth Paruchuri
Praneeth Paruchuri

Reputation: 1032

You can do everything together without using return or setting to another const:

const realNumberArray = [4, 5.6, -9.8, 3.14, 42, 6, 8.34, -2];

const squareList = (arr) => arr.filter(num => num > 0 && 
   Number.isInteger(num)).map(num => Math.pow(num,2));

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370799

Put the .map after the .filter is completely done:

const realNumberArray = [4, 5.6, -9.8, 3.14, 42, 6, 8.34, -2];
const squareList = (arr) => {
  return arr
    .filter(num => num > 0 && Number.isInteger(num))
    .map(num => num ** 2)
};

console.log(squareList(realNumberArray));

Upvotes: 2

Related Questions