Using JavaScript regex to match repeating subgroups

Can a regular expression capture all repeating and matching subgroups in one call?

Consider a string like this:

{{token id=foo1 class=foo2 attr1=foo3}}

Where the number of attributes (e.g. id, class, attr1) are variable and can be any key=value pair.

Currently, the regex and output are as follows here

var pattern = /\{{([\w\.]+)(?:\s+(\w+)=(?:("(?:[^"]*)")|([\w\.]+)))*\}\}/;
var str = '{{token arg=1 id=2 class=3}}';

var matches = str.match(pattern);
// -> ["{{token arg=1 id=2 class=3}}", "token", "class", undefined, "3"]

It appears to only match the last group; Is there a way to retrieve all the other "attributes" (arg and id)?

Please note: this example demonstrates matching in a single string, but the pattern may appear in a much larger string with multiple matches. Therefore, ^ and $ cannot be used.

Answer №1

It is not feasible to achieve this using a single regular expression. The JavaScript Regex function will only provide you with the last matched group, which is the main issue you are facing. I encountered a similar issue some time ago: Regex only capturing last instance of capture group in match. While it is possible to accomplish this in .Net, it may not be the ideal solution for your needs.

I am confident that with some effort, you can devise a regular expression to extract the arguments from the second group.

\{\{(\w+)\s+(.*?)\}\}

Below is a snippet of JavaScript code demonstrating how this can be achieved:

var input = $('#input').text();
var regex = /\{\{(\w+)\s*(.*?)\}\}/g;
var match;
var attribs;
var kvp;
var output = '';

while ((match = regex.exec(input)) != null) {
    output += match[1] += ': <br/>';

    if (match.length > 2) {
        attribs = match[2].split(/\s+/g);
        for (var i = 0; i < attribs.length; i++) {
            kvp = attribs[i].split(/\s*=\s*/);
            output += ' - ' + kvp[0] + ' = ' + kvp[1] + '<br/>';       
        }
    }
}
$('#output').html(output);

jsFiddle

An unconventional approach would be to utilize a regex and replace method to convert your code into JSON format, which can then be decoded using JSON.parse. The following snippet provides a starting point for this concept.

/[\s\S]*?(?:\{\{(\w+)\s+(.*?)\}\}|$)/g.replace(input, doReplace);

function doReplace ($1, $2, $3) {
  if ($2) {
    return "'" + $2 + "': {" + 
      $3.replace(/\s+/g, ',')
        .replace(/=/g, ':')
        .replace(/(\w+)(?=:)/g, "'$1'") + '};\n';       
    }
   return '';
 }

REY

Answer №2

If you want to achieve this functionality, you can follow these steps:

let str = "{{token id=foo1 class=foo2 attr1=foo3 hi=we}} hiwe=wef";
let matches = str.match(/(\w+(?==\w+)|(?!==\w+)\w+)(?!\{\{)(?!.*token)(?=.*}})/g);
matches.splice(0,1);
for (let i = 0; i < matches.length; i++) {
    console.log(matches[i]);
}

The regular expression used here is

/(\w+(?==\w+)|(?!==\w+)\w+)(?!\{\{)(?!.*token)(?=.*}})/g
(Make sure to use global modifier g to match all attributes)

After running the code, the array will be as follows:

["id","foo1","class","foo2","attr1","foo3","hi","we"]

For a demonstration, you can check out this live demo: http://jsfiddle.net/HYW72/1/

Answer №3

const strValue = "{{token id=foo1 class=foo2 attr1=foo3}}"
if (matches = strValue.match(///^
        \{\{
        ([a-z][a-z0-9]*)   # identifier
        (
            (?:
                \s+
                ([a-z][a-z0-9]*)  # identifier
                =
                (\S*)             # value
                )*
            )
        \}\}
        $///)
    [_, token, attrString] = matches

    const attributeMap = {}
    for (match of attrString.matchAll(///
            ([a-z][a-z0-9]*)  # identifier
            =
            (\S*)             # value
            ///g)
        [_, key, value] = match
        attributeMap[key] = value

    console.log "token = '#{token}'"
    console.log attributeMap
else
    console.log "NO MATCH"

This code is written in CoffeeScript, designed for better readability. The use of matchAll() function allows for parsing the attribute-value pairs efficiently. The regex pattern /// runs until the next ///, ignoring whitespace and allowing comments. There are some assumptions made about input data, like keys being identifiers with only lowercase letters, values being any non-whitespace characters, and unique attribute names, but they can be easily adjusted.

Just so you know, the output of the above code is:

token = 'token'
{ id: 'foo1', class: 'foo2', attr1: 'foo3' }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Attempting to transmit information to database using AJAX in the context of CodeIgniter

I'm having some trouble with my AJAX setup. It doesn't seem to be posting any data to the database, despite trying various solutions I found online. That's why I've turned to this platform for help. When testing in Postman and sending ...

Prevent animations on child elements with Vue.js

I am dealing with a scenario where I want to remove the fade transition on a child div within a <transition> component. The reason for nesting it is to prevent layout flickering, which can be demonstrated in a fiddle if necessary. In the fiddle belo ...

Discovering the specific marker that was clicked from a group of Google map markers

I have a collection of marker objects named markers. I am currently utilizing a for loop to assign event listeners to each one. However, I am encountering difficulty in determining which specific marker was clicked. This is the current code snippet I have ...

Activate ajax search in select2 by hand

I recently integrated the select2 plugin with jQuery into my website. For the most part, it functions perfectly. One particular feature I have is a search widget that utilizes select2 and remote data search. When I enter a search query using a keyboard ...

how can a select dropdown be dynamically displayed based on the previous selection?

If the first dropdown is set to "Professor" I want to display a second dropdown, but if it is set to "Student" then I do not want to display the second dropdown. function checkPrivilege() { var privilege = document.getElementById("permisija5").value; ...

Can someone provide guidance on effectively implementing this JavaScript (TypeScript) Tree Recursion function?

I'm currently grappling with coding a recursive function, specifically one that involves "Tree Recursion". I could really use some guidance to steer me in the right direction. To better explain my dilemma, let's consider a basic example showcasi ...

Listening for JS events on a CSS class called "int-only" which only accepts

Having an issue: I'm encountering a problem with this PHP code: <?php for($i = 0; $i < sizeof($floating_ips_json["floating_ips"]); $i++){ ?> <tr class="details-control-<?php echo $i; ?> cursor-pointer"> <t ...

Comparison of WebAPI Response Codes: Understanding the Difference Between 401 and

As a part of my learning project, I am developing a WebAPI and striving to implement best practices. The initial focus is on creating an authentication API that accepts an authentication object in JSON format: { username: myusername, password: mypa ...

Top method for establishing a mysql connection in Express version 4

Recently, I installed node-mysql and started running on express 4. Although I'm new to express, I am eager to learn the best practices for handling database connections. In my current setup, I have app.js var mysql = require('mysql'); //se ...

Angular version 4 is used to retrieve deeply nested JSON data

How do I extract data from a nested JSON file? Here is an example of the JSON structure: { "user1": { "name": "john", "surname": "johnsson" }, "user2": { "name": "Jacob", "surname": "Jacobsson" } } I want t ...

What is the best way to deduct pixels from numbers using JavaScript?

Currently, I am attempting to adjust the height of the footer based on the height of another div element. My approach involves utilizing the .css("height") function. However, I am encountering difficulty as the function does not seem to return the value i ...

The backend is not receiving the variable through RESTful communication

I'm attempting to pass the word "hello" to the backend of my code using the URL. However, instead of sending the string "hello" to my Java backend code, it's sending an empty string. Below is my backend code: @GET @Path("getJob/{stepName}") @Pr ...

Attempting to control an array of objects

In my current records: The parts with IDs 14.3, 14.2, and 14.1 belong to part ID = 30. The goal is to achieve the following: 1) By default, the first two IDs will be selected. If a user tries to select ID = 71, which belongs to part 30, they should not ...

Arrange DIV elements sequentially with HTML, JQuery, and CSS

Live Demo: Live Demo HTML: <div class="target"> <img src="bg-clock.png" alt="jQuery" /> </div> <div class="target2"> <img src="bg-clock.png" alt="jQuery" /> </div> CSS: .target, .target2 { ...

Issues with Ajax calls not functioning properly within CakePHP

I'm attempting to make an AJAX request in CakePHP. The submit button is marked as #enviar and the action as pages/contato. This is the code for my AJAX request: $(document).ready(function() { $('#enviar').click(function(){ $. ...

Is it possible to use @ViewChild to target an element based on its class name?

The author of this article on Creating Advanced Components demonstrates selecting an element by creating a directive first: @Directive({ selector: '.tooltip-container' }) export class TooltipContainerDirective {} Then, the author uses this d ...

The Hull.js Node.js npm module now offers convex hull computations instead of concave hull calculations

For my project, I am utilizing the node.js module called hull.js to compute a concave hull. However, when following the steps outlined in the "How it works" section on this link, the algorithm seems to halt at the 2nd step, resulting in a convex hull ins ...

Use $.ajax to display the menu by capturing an array of elements {0}

Whenever I click on one of the DIV elements, a menu pops out on the side displaying more information about the clicked element. I can determine which element I'm clicking on, but I'm struggling to pass that information from jQuery to the PHP pag ...

Receive JSON data with camel-case in a Web API 2.0 using a model in pascal-case style

My attempt to execute a PUT call on my Web API involves configuring the WebApiConfig.cs file to send data back to my Web project in camel case format. config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesCont ...

What is the process for requesting a specific condition using jscript and jquery?

I'm experimenting with the following code: while (true) { $(document).ready(function() { setInterval(function () { if ($("h2").text() == "What is a light-year?") { $("#choice2").delay(200).queue ...