3
\$\begingroup\$

I am trying to write a function to determine whether an array contains consecutive numbers for at least N numbers. For example, the input is [1,5,3,4] and 3, it turns true because the array has 3 consecutive numbers, which is [3,4,5]

Here this function requires sorting beforehand and it is not the most eloquent solution in my opinion. Can someone take a look and suggest some improvements on this?

function hasConsecutiveNums(array, N) {
  if (array.length < N) return false;
  if (N === 0) return true;
  const sortedArray = array.slice().sort((a, b) => a - b);
  let count = 0;
  let prev = null;
  for (const num of sortedArray) {
    if (prev && num === prev + 1) {
      count++;
    } else {
      count = 1;
    }
    if (count === N) return true;
    prev = num;
  }

  return false;
}

console.log(hasConsecutiveNums([1, 4, 5, 6], 3)) // true
console.log(hasConsecutiveNums([1, 4, 5, 6], 4)) // false

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Would the case [1, 4, 5, 5, 6], 3 be expected to return true? in the current code it doesn't \$\endgroup\$
    – Shaun H
    Apr 21 '20 at 8:56
3
\$\begingroup\$

One issue to consider: what if an element in the array is 0, and thus falsey? Then if (prev && will not be fulfilled:

console.log(hasConsecutiveNums([-1, 0, 1], 3)) // false... oops

function hasConsecutiveNums(array, N) {
  if (array.length < N) return false;
  if (N === 0) return true;
  const sortedArray = array.slice().sort((a, b) => a - b);
  let count = 0;
  let prev = null;
  for (const num of sortedArray) {
    if (prev && num === prev + 1) {
      count++;
    } else {
      count = 1;
    }
    if (count === N) return true;
    prev = num;
  }

  return false;
}

console.log(hasConsecutiveNums([-1, 0, 1], 3)) // false... oops

Another tweak to make the code a bit more elegant would be to assign prev to the first element of the array first, and initialize count to 1, thus starting comparison on the second element rather than on the first, avoiding the need to compare against null.

With this method, you also need to return true immediately if the array's length is only 1, like the other answer recommends, otherwise there won't be any iterations within which return true could be reached:

function hasConsecutiveNums(array, N) {
  if (array.length < N) return false;
  if (N <= 1) return true;
  const sortedArray = array.slice().sort((a, b) => a - b);
  let prev = sortedArray.shift();
  let count = 1; // first element of the array is already in prev
  for (const num of sortedArray) {
    if (num === prev + 1) {
      count++;
    } else {
      count = 1;
    }
    if (count === N) return true;
    prev = num;
  }

  return false;
}

console.log(hasConsecutiveNums([1, 4, 5, 6], 3)) // true
console.log(hasConsecutiveNums([1, 4, 5, 6], 4)) // false
console.log(hasConsecutiveNums([-1, 0, 1], 3)) // true

If, as the comment notes, you'd want [1, 2, 2, 3] to return true, de-duplicate the numbers with a Set:

function hasConsecutiveNums(array, N) {
  if (array.length < N) return false;
  if (N <= 1) return true;
  const sortedArray = [...new Set(array.slice().sort((a, b) => a - b))];
  let prev = sortedArray.shift();
  let count = 1; // first element of the array is already in prev
  for (const num of sortedArray) {
    if (num === prev + 1) {
      count++;
    } else {
      count = 1;
    }
    if (count === N) return true;
    prev = num;
  }

  return false;
}

console.log(hasConsecutiveNums([1, 4, 5, 6], 3)) // true
console.log(hasConsecutiveNums([1, 4, 5, 6], 4)) // false
console.log(hasConsecutiveNums([-1, 0, 1], 3)) // true
console.log(hasConsecutiveNums([1, 2, 2, 3], 3)) // true

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for incorporating my comment into you answer, keeps my lazy butt for having to write my own :P \$\endgroup\$
    – Shaun H
    Apr 22 '20 at 11:22
1
\$\begingroup\$

Some quick remarks:

if (N === 0) return true;

can be replaced by

if (N <= 1) return true;

because in a non-empty array each element is a single “consecutive number.” This saves the sorting of the array in the case \$ N = 1 \$.

Then it suffices to check the count again only if it has been incremented.

The comparison with the previous number (or null) can be simplified slightly.

  for (const num of sortedArray) {
    if (prev === num - 1) {
      count++;
      if (count === N) return true;
    } else {
      count = 1;
    }
    prev = num;
  }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.