🟨 JavaScript
Medium
🕐 15 min

Group numbers into ranges

Goal: to practice array manipulation, sorting, and data grouping.

💡 Solution Hint
  1. First, sort the array of numbers in ascending order.
  2. To handle duplicates, you can create a Set from the array and then convert it back to an array.
  3. Iterate through the sorted array. You will need a variable to store the start of the current range.
  4. In each iteration, check if the next number in the array is consecutive (i.e., current + 1).
  5. If the sequence is broken (or you have reached the end of the array), the current range is complete.
  6. If the start and end of the range are the same, it’s a single number. If not, it’s a range start-end.
  7. Collect the resulting strings (single numbers and ranges) into an array, and at the end, join them with a comma.
  8. Don’t forget to handle the case of an empty input array.
👀 Solution
const range = (numbers) => {
  // Step 1: Handle empty array.
  // If the array is empty, return an empty string as required.
  if (numbers.length === 0) return '';
 
  // Step 2: Prepare the data.
  // Create a `Set` from the array to automatically remove duplicates.
  // `Array.from` converts the `Set` back to an array.
  // `.sort((a, b) => a - b)` sorts the numbers in ascending order.
  const sortedUniqNumbers = Array.from(new Set(numbers))
    .sort((a, b) => a - b);
  
  // Step 3: Initialize variables.
  // `ranges` is an array to store the final strings (numbers and ranges).
  const ranges = [];
  // `start` stores the starting number of the current range.
  let start = sortedUniqNumbers[0];
  // `prev` stores the previous number to check for sequence.
  let prev = start;
 
  // Step 4: Iterate through the array to find ranges.
  // Start from the second element (i=1) and go to the end of the array (including a "virtual" element after the last one).
  // This allows processing the last range without extra code after the loop.
  for (let i = 1; i <= sortedUniqNumbers.length; i++) {
    // `current` is the number being considered.
    const current = sortedUniqNumbers[i];
    
    // Step 5: Check if the sequence continues.
    // If the current number is 1 greater than the previous, we are still in a range.
    if (current === prev + 1) {
      // Update `prev` and move to the next number.
      prev = current;
      continue;
    }
    
    // Step 6: The sequence is broken. Format the string for the completed range.
    // If `start` and `prev` are equal, the range consisted of a single number.
    if (start === prev) {
      ranges.push(String(start));
    } 
    // If the range had two numbers (e.g., 5, 6), they do not form a "5-6" range.
    // Add them as two separate numbers.
    else if (start + 1 === prev) {
      ranges.push(String(start), String(prev));
    } 
    // If there are three or more numbers in the range, format it as `start-prev`.
    else {
      ranges.push(`${start}-${prev}`);
    }
    
    // Step 7: Reset variables for the next potential range.
    // The new start is `current`, the number that broke the previous sequence.
    start = current;
    prev = current;
  }
 
  // Step 8: Return the result.
  // Join all strings from the `ranges` array into one, separated by commas.
  return ranges.join(',');
};

Solution Analysis:

This approach to solving the problem can be broken down into several key stages:

  1. Edge Case Handling: The function first checks if the input array is empty. If so, it immediately returns an empty string as required. This is a simple but important optimization that prevents unnecessary code execution.

  2. Data Preparation (Cleaning and Sorting):

    • Duplicate Removal: The input data may contain duplicate numbers. To treat them as a single number, we use a Set, which by its nature stores only unique values. Creating new Set(numbers) and then converting it back to an array with Array.from() is an elegant and efficient way to remove duplicates.
    • Sorting: To find consecutive numbers, the array must be sorted. The .sort((a, b) => a - b) method ensures that the numbers are arranged in ascending order, which is a necessary condition for our algorithm.
  3. Iterative Range Finding:

    • Initialization: We initialize three variables: ranges to store the final strings, start to mark the beginning of the current range, and prev to store the previous element.
    • Loop: We iterate through the sorted array, starting from the second element. The key idea here is to compare the current element (current) with the previous one (prev).
    • Range Detection Logic:
      • If current is one greater than prev, it means the sequence continues. We simply update prev and move to the next iteration.
      • If the sequence is broken (the numbers are not consecutive) or we have reached the end of the array, we need to “close” the current range.
    • Output Formatting: When closing a range, we check:
      • If start and prev are equal, it was a single number.
      • If the range had only two numbers (start + 1 === prev), we add them separately, not as a range (e.g., 5,6 instead of 5-6).
      • If the range had three or more numbers, we format it as start-prev.
  4. Result Assembly: After the loop completes, the ranges array contains all the necessary strings ('0-5', '8', '9', '11'). The join(',') method combines them into the final string, which the function returns.

  • Time Complexity: O(N log N), where N is the number of elements in the array. The main bottleneck is sorting (.sort()). The loop itself iterates through the array once, which is O(N), but sorting dominates.
  • Space Complexity: O(N) to store the sorted array without duplicates and the ranges array with the results. In the worst case (when there are no ranges), both arrays will have a size comparable to the original array.

Task Description

Write a function range that takes an array of integers and returns a string representing the sorted and grouped ranges of these numbers.

The numbers in the string should be sorted in ascending order. Consecutive numbers should be grouped into a range, for example, 1, 2, 3, 4 becomes 1-4. If a number is not part of any range, it remains as is.

Examples

range([1, 4, 5, 2, 3, 9, 8, 11, 0]);
// Returns: '0-5,8,9,11'
 
range([-1, 0, 1, 5, 6]);
// Returns: '-1-1,5,6'
 
range([1, 3, 5]);
// Returns: '1,3,5'

Requirements

  • The function must be named range.
  • It must accept an array of numbers.
  • It must return a string.
  • The input array may contain duplicates, which should be treated as a single number.
  • The array may not be sorted.
  • If the input array is empty, the function should return an empty string.

🧑‍💻 It's not a bug! It's a feature!

The code editor is intentionally hidden on mobile.

Believe me, it's for the best: I am protecting you from the temptation to code in less-than-ideal conditions. A small screen and a virtual keyboard are not the best tools for a programmer.

📖 Now: Study the task, think through the solution. Act like a strategist.

💻 Later: Sit down at your computer, open the site, and implement all your ideas comfortably. Act like a code-jedi!