efw
efw

Reputation: 469

Refactor from Procedural Paradigm to Functional Paradigm

In the process of learning functional programming, I am trying to refactor the following code using map, filter, and/or reduce.

I see that I can deal with the conditional using the filter method, but don't know how to handle the repeated assignment so I can avoid use of a for loop.

I am thinking that I would use the map method to handle the diff assignments, and chain a filter method that would deal with the conditional. Am I on the right track?

Would someone be kind enough to refactor the following code in the functional paradigm and explain. Thanks.

This function finds the first non-consecutive number in an array.

 function firstNonConsecutive (arr) {
   var diff = 0;
    for(var i = 0; i < arr.length; i++) {
        diff = arr[i+1] - arr[i];
        if(diff > 1) {
            return arr[i+1];
        }
    }
    return null;

Upvotes: 2

Views: 87

Answers (4)

Mulan
Mulan

Reputation: 135227

Recursion by mathematical induction -

  1. If the first element, a, or the second element, b, are null, return undefined
  2. (induction) Neither the first element, a, nor the second element, b are null. If b is consecutive to a, return the recursive result of the smaller problem
  3. (induction) Neither the first element, a, nor the second element, b, are null and b is not consecutive to a. Return the answer, b.

const firstNonConsecutive = ([ a, b, ...more ]) =>
  a == null || b == null
    ? undefined                            // 1
: b === a + 1
    ? firstNonConsecutive([ b, ...more ])  // 2
: b                                        // 3

console.log(firstNonConsecutive([ 4, 5, 6, 8, 9, 10 ]))
// 8

console.log(firstNonConsecutive([ 7, 8, 9, 10, 13, 14 ]))
// 13

console.log(firstNonConsecutive([ 99 ]))
// undefined

console.log(firstNonConsecutive([]))
// undefined

Upvotes: 0

GirkovArpa
GirkovArpa

Reputation: 4912

You want to use Array.find, not .map, .filter, or .reduce. You could find a way to use those, but they waste time because they don't return as soon as the first match is found.

Here are some progressively more verbose solutions to help you understand how the first one works.

The second is the most functional, because it's declarative unlike the first.

array.find(nonConsecutive) reads close to plain English and declares what you want to do, leaving the imperative implementation details hidden away inside the nonConsecutive function.

const array = [1, 2, 3, 4, 5, 6, 7, 9, 10];

console.log(
  array.find((n, i) => i && n != array[i - 1] + 1) // 9
);

const nonConsecutive = (n, i, arr) => i && n != arr[i - 1] + 1;
console.log(
  array.find(nonConsecutive) // 9
);

console.log(
  array.find(function (number, index) { // we don't need third "array" argument because the array is already in scope.
    if (index == 0) return false; // if index is zero, return. Otherwise, next line would access index -1.
    if (number != array[index - 1] + 1) return true; // if number is not equal to the the previous number, plus one, it's not consecutive. Return it.
    return false; // if we reach this line than the number must be consecutive, so return false.
  }) // 9
);

Upvotes: 0

Hitmands
Hitmands

Reputation: 14179

If you are looking into fp, then also recursion would find a good application here:

const firstNonConsecutive = (list) => {
  if (!list.length) { 
    // list is empty, not found
    return -1;
  }
  
  const [head, ...tail] = list;
  const [n] = tail;
  
  if (n - 1 !== head) { 
    // found
    return n;
  }
  
  // yet another round
  return firstNonConsecutive(tail);
};

console.log(
  firstNonConsecutive([1, 2, 3, 4, 5, 6, 7, 9, 10]),
);

Upvotes: 0

Milton
Milton

Reputation: 1012

Consider using Array.find https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find, like most functional array functions it takes a callback/predicate that takes 3 parameters, the item, the index and the whole array. With this you can look ahead/behind in the same way you currently do. Like this:

 function firstNonConsecutive2(arr) {
   return arr.find((item, index, array) => {
     const diff = array[index - 1] - item; // Use index -1 to look behind instead of ahead since we want to return/find the item that is non-consecutive
     return diff === 0; // I think this was a small bug in your version which worked if the numbers were incrementing only
   });
 }

In the first iteration of the find "loop" it'll try to diff undefined with for example 1, which is NaN, NaN is not equal to 0 so it keeps searching. Next it'll try maybe 1 and 2 so diff becomes -1 so it keeps searching. Until it reaches for example 5 and 5, which diffs to 0 so the find predicate is now true, so it will return the second 5 since that is the current item, we're looking behind us by using index - 1.

Let me know if you want further explanation of something!

Upvotes: 1

Related Questions