'Can someone help me with this javascript regex to turn a string of list items into an array with the item texts only (no list item # or new line)

I have 2 kinds of strings coming back from an API. They look as follows:

let string1 = "\n1. foo\n2. bar\n3. foobar"
let string2 = "\n\n1. foo.\n\n2. bar.\n\n3. foobar."

To be clear, string 1 will always have 3 items coming back, string 2 has an unknown number of items coming back. But very similar patterns.

What I want to do is pull out the text only from each item into an array.

So from string1 I want ["foo", "bar", "foobar"] From string2 I want ["foo.", "bar.", "foobar."]

I'm awful at regex but I somehow stumbled myself into an expression that accomplishes this for both string types, however, it uses regex's lookbehind which I'm trying to avoid as it isn't supported in all browsers:

let regex = /(?<=\. )(.*[a-zA-Z])/g;
let resultArray = str.match(regex);

Would someone be able to help me refactor this regex into something that doesn't use lookbehind?



Solution 1:[1]

Some notes about the pattern (?<=\. )(.*[a-zA-Z]) that you tried:

  • The first part asserts a dot and space to the left, but does not take any newlines into account so it could possibly also match on other positions

  • It does not take the digits into account of matching a list item

  • The second part of your pattern matches till the last occurrence of a character A-Za-z which does not match ending dot in the examples in string2


All your example strings start with a newline, a number, a dot and 1 or more spaces.

You can get the matches without using a lookbehind, and make the pattern more specific by starting the match with the list item format.

Then capture the rest of the line after it in a capture group.

\n\d+\.[^\S\r\n]+(.+)

Explanation

  • \n Match a newline
  • \d+\. Match 1+ digits and a dot
  • [^\S\r\n]+ Match 1 or more spaces without newlines
  • (.+) Capture group 1, match 1 or more chars

See a regex demo.

const regex = /\n\d+\.[^\S\r\n]+(.+)/g;
[
  "\n1. foo\n2. bar\n3. foobar",
  "\n\n1. foo.\n\n2. bar.\n\n3. foobar."
].forEach(s => {
  console.log(Array.from(s.matchAll(regex), m => m[1]))
});

If the string should also match without a leading newline, you can use an anchor ^ to assert the start of the string and use the multiline flag /m

const regex = /^\d+\.[^\S\r\n]+(.+)/gm;
[
  "1. foo\n2. bar\n3. foobar",
  "1. foo.\n\n2. bar.\n\n3. foobar."
].forEach(s => {
  console.log(Array.from(s.matchAll(regex), m => m[1]))
});

Solution 2:[2]

If I understood you correctly, then perhaps this solution can help you:

const string1 = "\n1. foo\n2. bar\n3. foobar";
const string2 = "\n\n1. foo.\n\n2. bar.\n\n3. foobar.";

const parse = (str) => {
  return str.split(/\n[0-9.\s]*/g).filter((item) => item !== "");
}

console.log('Example 1:', parse(string1));
console.log('Example 2:', parse(string2));

Solution 3:[3]

EDIT: see @Oleg-Barabanov 's solution, it's technically a bit quicker.

string.replace(/\n[0-9.]*/g, "").split(" ").slice(1)
  • \n for the new line
  • 0-9 for digits (also could use \d
  • . for the dot after the number
  • g to replace all
  • .split(" ") to chop it up wherever there's a space (\s)

Demo:

let string1 = "\n1. foo\n2. bar\n3. foobar"
let string2 = "\n\n1. foo.\n\n2. bar.\n\n3. foobar."

const parse = (str) => str.replace(/\n[0-9.]*/g, "").split(" ").slice(1)


console.log(parse(string1))
console.log(parse(string2))

Solution 4:[4]

I'm injected a Security instance in the DataProvider __construct so I can fetch the logged User!

Now I can use $security->getUser().

namespace App\DataProvider;

use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use App\Entity\Order;
use Symfony\Component\Security\Core\Security;

final class OrderItemDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface {
    
private $security = null;

public function __construct(Security $security)
{
        $this->security = $security;
}

public function supports(string $resourceClass, string $operationName = null, array $context = []): bool {
        return Order::class === $resourceClass;
}

public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?Order {
        return new Order($id);
}

}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Oleg Barabanov
Solution 3
Solution 4 Daniele