2

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)
Code Maniac
  • 37,143
  • 5
  • 39
  • 60
  • How do you define a valid number [expression]? In JS, there are quite a few non-decimal representations that you might not think of typically as numbers which commonly appear in source code and parse as decimal values (e.g. `2e3` (`2000`), `0xff` (`255`), etc.) – jsejcksn Jul 29 '22 at 12:05

3 Answers3

2

You can simply split on , and check if it's a valid number of not

function extractNumbers(str) {
  let onlyNumbersString = str.split(',').filter(v => /^\d+(\.\d+)?$/.test(v))
  console.log(onlyNumbersString)
}


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)
extractNumbers('12.3,22,Q')

If you don't want to use regex, you can use isNaN

let onlyNumbersString = str.split(',').filter(v => v.trim() && !isNaN(v))
Code Maniac
  • 37,143
  • 5
  • 39
  • 60
  • 2
    `'1,,3'.split(',').filter(v => !Number.isNaN(+v))` evaluates to `['1', '', '3']`. Is `''` a valid number? – jsejcksn Jul 29 '22 at 12:11
  • @jsejcksn nice observation, with given example i don't think this will be case, but if needed after split we can simply filter empty values as well, which will save us from this situation – Code Maniac Jul 29 '22 at 12:13
1

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.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

I think you can build the desired output by using the following regex

/\b\d+\.?\d*\b/g

This is assuming there are no strings like Name2 45

const str = "12.3,Name1,3,4,Name2,35,Name3";

console.log(str.match(/\b\d+\.?\d*\b/g))
Ele
  • 33,468
  • 7
  • 37
  • 75
  • You can solve the `Name2 45` issue by changing the regex to use lookaround: `/(?<=^|,)\d+\.?\d*(?=$|,)/g` Won't work on obsolete JavaScript engines without lookbehind of course. Combine that with converting to number and filtering out blanks/invalid, and: `console.log(str.match(/(?<=^|,)\d+\.?\d*(?=$|,)/g).map((s) => s.trim() ? Number(s) : NaN).filter((n) => !isNaN(n)));` – T.J. Crowder Jul 29 '22 at 17:27