2

I'm trying to get all numbers inside a string. This can include either whole or rational numbers. I did the hard work of coming up with the regex.

numRegex = /^\d{1,}$|^\d{1,}\.\d{1,}$/

This regex tests if a string is a number.

I have this string

string = "65-KH-ON-PEAK|2.1-K1-ON-PEAK|164-KH-OFF-PEAK|27-K1"

I'm trying to return the following in an array

["65","2.1","1", "164","27", "1"];

If you run the test function on all of those numbers, you will get true returned.

Example

var numbers = ["65", "2.1", "1", "164", "27", "1"];
numbers.every(function(number) {
    return numRegex.test(number);
});

=> true

How do I get that output with my regular expression?

You can't use the match function because that only returns the first instance.

Edit: I tried changing the regex by removing the start and end anchors and adding the global flag. I can't believe I missed that. It does return an array, but it returns too many elements

=> ["65", "2", "1", "1", "164", "27", "1"]
Richard Hamilton
  • 25,478
  • 10
  • 60
  • 87
  • `/(?:^|\|)[0-9]+(\.[0-9]+)?/` – Sverri M. Olsen Dec 08 '15 at 03:36
  • [`\b(\d+(?:\.\d+)?)\b`](https://regex101.com/r/hE1iD8/2) – Tushar Dec 08 '15 at 03:38
  • I think you missunderstand my question. I already have the regex. I need to create an array with all substrings that satisfy my regex – Richard Hamilton Dec 08 '15 at 03:41
  • @RichardHamilton I don't understand the point of rechecking the results using regex which are get from the regex, that's always going to return `true` – Tushar Dec 08 '15 at 03:42
  • That was only an example to show that all those numbers matches my regex. I need to extract them from the string – Richard Hamilton Dec 08 '15 at 03:45
  • 1
    To get all matches use `g` flag – Tushar Dec 08 '15 at 03:46
  • I tried `g`, but that would be too many matches, since he wants only the first digits. He's better off splitting by the pipe and then match the first few digits in a loop. – starlocke Dec 08 '15 at 03:47
  • Yep. I updated the post to give the output – Richard Hamilton Dec 08 '15 at 03:53
  • Switch the order of your regex. When given an alternation, if the first pattern matches it won't even try the second. So given `1.2`, your current regex is perfectly happy to match the `1` and then the `2`. Swap the patterns and it will try to match `1.2` first, and then fall back to a whole number if it can't match a decimal. – JDB Dec 08 '15 at 04:08

4 Answers4

3

Based on the new target output... a very simple global match as originally commented upon.

"65-KH-ON-PEAK|2.1-K1-ON-PEAK|164-KH-OFF-PEAK|27-K1".match( /([0-9.]+)/g )

["65", "2.1", "1", "164", "27", "1"]

starlocke
  • 3,407
  • 2
  • 25
  • 38
  • This is clearly the optimal solution. Had no idea it would be that simple. Excellent one liner – Richard Hamilton Dec 08 '15 at 04:17
  • 1
    @RichardHamilton: Note that this solution will **also** match strings like `.`, `6.5.2`, `1...8` and so on. This may or may not be a problem depending on the format of your input. – Nisse Engström Dec 08 '15 at 18:00
1

Try using numRegex.exec. RegExp.prototype.exec returns an array of matches (or null) as opposed to a boolean. MDN has a good page about it: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec

Hatchet
  • 5,320
  • 1
  • 30
  • 42
1

Like I said in comment: split the string, build an array via loop. You haven't shown a good reason to require exclusively a regex solution.

In plain JS:

var input = "65-KH-ON-PEAK|2.1-K1-ON-PEAK|164-KH-OFF-PEAK|27-K1";
var words = input.split("|");
var ids = [];
for( var i = 0; i < words.length; ++i) {
    var w = words[i];
    var digits = w.match(/[0-9.]+/);
    ids.push(digits[0]);
}
console.log(ids);

https://jsfiddle.net/pncsbz7j/1/

(╯•﹏•╰) I feel like I'm doing somebody's homework, here.

Note - the original question appeared to need extraction of only the first few digits per "id number", as opposed to every set of digits.

starlocke
  • 3,407
  • 2
  • 25
  • 38
  • There is no lodash tag on this question. Please keep it to vanilla js unless there is an appropriate framework tag on the question. –  Dec 08 '15 at 04:15
  • Today I learned that plain JS handles string split trivially. #ShockingTruth ;) – starlocke Dec 08 '15 at 04:26
1

This is so much simpler than you think it is. Swap the alternates:

\d{1,}\.\d{1,}|\d{1,}$

Done. With regex, if the first alternate produces a match, then the regex engine will not even try the second. Because your first pattern was a subset of your second, it follows that the second would never ever match, because the first would always win. By swapping them, you given the longer regex a chance to match before falling back to the shorter.

JDB
  • 25,172
  • 5
  • 72
  • 123