Legend
Legend

Reputation: 15

Generate a list of multiples in a functional way

This code returns multiple of 5. I am trying to do it in a functional way. Is it functional?

function Mul(start,array,Arr)
{
    Arr[start]=array[start]*5;
 
    if(start>array.length-2){
    return Arr;
    }
   
    return Mul(start+1,array,Arr);
 }    
var numbers =[1,2,3,4,5,6,7,8,9,10];
var Arr=[]; 

I am passing an empty array and storing values in it.

console.log("table ", Mul(0,numbers,Arr));

Upvotes: 1

Views: 268

Answers (3)

Hitmands
Hitmands

Reputation: 14179

Let's not forget that also recursion comes in handy here!

const multipleOf = (factor) => (length) => {
  if (!length) { return []; }
  
  return [
    ...multipleOf(factor)(length - 1),
    length * factor
  ];
};


const by5 = multipleOf(5);
const by2 = multipleOf(2);

console.log(
  by5(10),
);

console.log(
  by2(50),
);

Upvotes: 0

customcommander
customcommander

Reputation: 18901

Your initial attempt wouldn't qualify as functional since it modifies its original input.

This isn't ideal because in addition to keeping track of the execution flow of a program you also need to worry about the data. Here's a contrived example:


const last = arr => arr.pop();
const sum = ([a, b]) => a + b;  

const x = [4, 2];
const y = [4, 2];

// Works as expected

sum(x);  //=> 6
last(x); //=> 2

// Bug!

last(y); //=> 2
sum(y);  //=> NaN

The issue here is that Array#pop modifies the array. We can observe that for the same input the order of execution now matters. This is unnecessary, leads to unstable programs and create bugs that while trivial to fix are sometimes very hard to spot.

If your intention is to multiply all elements in your array with 5 then the simplest solution yet fully functional is to do xs.map(x => x * 5).

In functional programming there's also a function called unfold that generates a list by applying a function on a value until a condition is met. It would look like this:

unfold(mult5, 1); // we start with 1 until 10 and we multiply by 5 along the way.
//=> [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]

But we don't need to go into these details if the boundaries are known in advance. i.e. you need the first n multiple of 5.

Let's "allocate" 10 spots for our multiples of five:

const xs = Array(10);
//=> An array of 10 empty values

Warning: you cannot map such an array!

const ys = xs.map(x => x * 5);
//=> Still an array of 10 empty values!

You need to use Array.from:

const ys = Array.from(xs, (_, i) => (i + 1) * 5);
//=> [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 370819

It's not functional, since you're mutating the argument.

A more functional method would be to use the built-in .map function to transform each element of the array:

const mult5 = arr => arr.map(item => item * 5);
const Mul = (start, input) => input.slice(0, start).concat(mult5(input.slice(start)));

console.log("table ", Mul(0, [1,2,3,4,5,6,7,8,9,10]));

(though, that admittedly isn't perfectly functional either, due to the console.log, which is a side-effect)

If you always transform the whole array, there's no need for start:

const Mul = arr => arr.map(item => item * 5);

console.log("table ", Mul([1,2,3,4,5,6,7,8,9,10]));

Upvotes: 2

Related Questions