ys4503
ys4503

Reputation: 27

Javascript with Regex to split string at different specific characters

I have the following string

Name=(Last, First), Age=(31 year, 6 months, 3 day), Height= 6.1 ft, Employment=None, Email Address =/NA/, Mobile=XXXX

and I would like to split them into the following to build a dictionary

Name: "(Last, First)"
Age: "(31 year, 6 months, 3 day)"
Height: " 6.1 ft"
...

I came across this post and tried to tweak it but can get it work with keys with/out "()". Here is the code or from this link. Would appreciate your help and feel free to suggest easier or alternative way.

txt="Name=(Last, First), Age=(31 year, 6 months, 3 day), Height= 6.1 ft, Employment=None, Email Address =/NA/, Mobile=XXXX"

//var r = /.+?=.+?(?=(\([^)]+\),)|(.=)|$)/g;
//var r = /.+?=.+?(?=(=(.*),)|$)/g;

var r = /.+?=.+?(?=(\),)|(\d\b,)|$)/g;

var obj = txt.match(r).reduce(function(obj, match) {
    var keyValue = match.split("=");
    obj[keyValue[0].replace(/,\s/g, '')] = keyValue[1];
    return obj;
}, {});
console.log(obj);

Upvotes: 1

Views: 55

Answers (2)

CertainPerformance
CertainPerformance

Reputation: 370679

To match the properties and values, you can use:

(\w+)\s*=\s*(\([^)]+\)|[^,]+)
  • (\w+) - Match and capture the property (one or more word characters)
  • \s*=\s* - Followed by optional spaces, a =, and more optional spaces
  • (\([^)]+\)|[^,]+) - Match and capture either:
    • \([^)]+\) - A (, followed by non-) characters, followed by ), OR
    • [^,]+ - Non-comma characters

const str = 'Name=(Last, First), Age=(31 year, 6 months, 3 day), Height= 6.1 ft, Employment=None, Email Address =/NA/, Mobile=XXXX';

const obj = {};
for (const [, prop, val] of str.matchAll(/(\w+)\s*=\s*(\([^)]+\)|[^,]+)/g)) {
  obj[prop] = val;
}
console.log(obj);

If the input keys may contain spaces as well, match anything but a =:

const str = 'Employee Name=(Last, First), Person Age=(31 year, 6 months, 3 day), Height= 6.1 ft, Employment=None, Email Address =/NA/, Mobile=XXXX';

const obj = {};
for (const [, prop, val] of str.matchAll(/(\w[^=]+)\s*=\s*(\([^)]+\)|[^,]+)/g)) {
  obj[prop] = val;
}
console.log(obj);

If you can't use matchAll, then iterate over the matches manually with exec:

const str = 'Name=(Last, First), Age=(31 year, 6 months, 3 day), Height= 6.1 ft, Employment=None, Email Address =/NA/, Mobile=XXXX';

const obj = {};
const pattern = /(\w+)\s*=\s*(\([^)]+\)|[^,]+)/g;
let match;
while (match = pattern.exec(str)) {
  const [, prop, val] = match;
  obj[prop] = val;
}
console.log(obj);

Upvotes: 1

Tim Biegeleisen
Tim Biegeleisen

Reputation: 520948

Here is one way, using a smart lookahead to split conditionally on only the correct commas:

var txt = "Name=(Last, First), Age=(31 year, 6 months, 3 day), Height= 6.1 ft, Employment=None, Email Address =/NA/, Mobile=XXXX"
var map = {};
var parts = txt.split(/,\s*(?![^(]*\))/);
parts.forEach(function (item, index) {
    map[item.split(/=/)[0]] = item.split(/=/)[1];
});
console.log(map);

The regex used for splitting requires an explanation:

,\s*         match comma with optional whitespace
(?![^(]*\))  assert that we cannot lookahead and see a closing ) without
             first seeing an opening (

The lookahead condition prevents matching commas which happen to be inside (...) terms. Then, once we have the array of terms, we iterate and build the dictionary you want, splitting each term on = to find the key and value.

Upvotes: 1

Related Questions