Search code examples
javascriptarraysstringextractnumeric

Advice on how to resolve an issue with extracting only numbers from a string using JavaScript


I'm new to stackoverflow and would appreciate some advice to help me solve this problem. I have two strings that I want to extract numbers from

String 1 = "12.3,Name1,3,4,Name2,35,Name3" returns [12.3,3,4,35]

Which is the required result.

String 2 = "12.3,22,Q" returns [12.322] Which is the incorrect result, It should be [12.3,22]

I have commented on my code with the steps I have taken to complete the task. Again many thanks for your advice and help in advance.

Here is a copy of my code:

function extractNumbers(str) {
  //Use replace() to replace all instances of letters/alpha*

  let onlyNumbersString = str.replace(/[a-z]/ig, '');

  //remove "," using replace()*

  onlyNumbersString = onlyNumbersString.replace(",", "");

  //Create an array of numbers* 
  let arrayOfNumbers = onlyNumbersString.split(',');


  let result = arrayOfNumbers.map((x) => parseFloat(x))
  console.log(arrayOfNumbers)
  console.log(result);

  const filtered = result.filter(function(e) {
    return e
  })
  console.log(filtered)
}


let numbers = "12.3,Name1,3,4,Name2,35,Name3" //returns [12.3,3,4,35]
//"12.3,22,Q" returns [12.322]*

extractNumbers(numbers)


Solution

  • The incorrect result is because of this line:

    onlyNumbersString = onlyNumbersString.replace(",", "");
    

    Once you do that, "12.3,22," (which was "12.3,22,Q" but had the Q removed before) becomes "12.322", so that looks like a single number.

    I'd split first, then remove the segments that have non-number text in them, then convert to number:

    function extractNumbers(str) {
        // Split on commas, since that seems to be the delimiter you want
        const parts = str.split(",");
    
        // Remove any strings that aren't all number chars (including `.`)
        const filtered = parts.filter((s) => !/[^.\d]/.test(s));
    
        // Convert to number
        const numbers = filtered.map((s) => parseFloat(s));
        return numbers;
    }
    
    console.log(extractNumbers("12.3,Name1,3,4,Name2,35,Name3"));

    Or without comments:

    function extractNumbers(str) {
        const parts = str.split(",");
        const filtered = parts.filter((s) => !/[^.\d]/.test(s));
        const numbers = filtered.map((s) => parseFloat(s));
        return numbers;
    }
    

    You could make it a single statement, but it becomes harder to debug:

    function extractNumbers(str) {
        return str
            .split(",")
            .filter((s) => !/[^.\d]/.test(s))
            .map((s) => parseFloat(s));
    }
    

    I should note that the only reason to check that a segment is valid is that parseFloat will happily accept "123abc", returning 123 and ignoring the rest. You could use the unary + operator or the Number function instead, checking for the "" case (which would give you 0 if you didn't weed it out):

    function extractNumbers(str) {
        const parts = str.split(",");
        const numbers = parts.map((s) => s.trim() ? +s : NaN);
        const validNumbers = numbers.filter((num) => !isNaN(num));
        return validNumbers;
    }
    
    console.log(extractNumbers("12.3,Name1,3,4,Name2,35,Name3"));

    My answer here goes through the various options for converting strings to numbers and the pitfalls of each of them.