Reputation: 43
I have a string parsed from a uint8array. something like
"[[[Class1(a1)],[Class2(a2)],[Price(a1,100)]],[[Class3(a3)],[Price(a3,200)]],[]]"
or
"[[],[[Class1(a1)],[Color(a1,200)]],[[IsLight(a1,0)]]]"
This is a 2D array with three fixed second level arrays => [ [], [], [] ]
, but the elements inside these three arrays are denoted using square brackets as well, which makes it very hard to find a pattern to use str.slice
. JSON.parse
doesn't work either.
Is there a way to actually convert this string to an array in Javascript?
Upvotes: 4
Views: 2858
Reputation: 28688
function parse(s) {
return JSON.parse(s
.replace(/(?<=\[)([^\[\]])/g, "\"$1")
.replace(/([^\[\]])(?=\])/g, "$1\""));
}
const s1 = "[[[Class1(a1)],[Class2(a2)],[Price(a1,100)]],[[Class3(a3)],[Price(a3,200)]],[]]";
console.log(parse(s1));
const s2 = "[[],[[Class1(a1)],[Color(a1,200)]],[[IsLight(a1,0)]]]";
console.log(parse(s2));
Here is how the regexes work:
This way everything inside brackets is wrapped into strings and the bracket structure can be parsed into an Array
hierarchy using JSON.parse
.
IMPORTANT: If you'd also want to run the functions in the strings, and this code runs in the browser, do not use eval
! Use a Web Worker instead, which runs in a separate context (here is how).
UPDATE
The code can be simplified to use a single replace
:
function parse(s) {
return JSON.parse(s.replace(/(?<=\[)([^\[\]]+)(?=\])/g, "\"$1\""));
}
const s1 = "[[[Class1(a1)],[Class2(a2)],[Price(a1,100)]],[[Class3(a3)],[Price(a3,200)]],[]]";
console.log(parse(s1));
const s2 = "[[],[[Class1(a1)],[Color(a1,200)]],[[IsLight(a1,0)]]]";
console.log(parse(s2));
Although this version is simpler and faster, it's still much slower than @Dave's parser: https://jsperf.com/https-stackoverflow-com-questions-63048607
Upvotes: 1
Reputation: 12036
Simple regex for that:
let x = "[[[Class1(a1)],[Class2(a2)],[Price(a1,100)]],[[Class3(a3)],[Price(a3,200)]],[]]";
x = x.replace(/([\w\)]){1}\]/g,'$1"').replace(/\[([\w]){1}/g,'"$1');
console.log(JSON.parse(x));
But if Class1()
, Price()
etc. are real functions, you can use for example eval()
(be extra cautious when using eval()
, may lead to code injections) for that:
let x = "[[[Class1(a1)],[Class2(a2)],[Price(a1,100)]],[[Class3(a3)],[Price(a3,200)]],[]]";
console.log(eval(x));
If u don't want additional []
around function results, you can merge the both:
let x = "[[[Class1(a1)],[Class2(a2)],[Price(a1,100)]],[[Class3(a3)],[Price(a3,200)]],[]]";
x = x.replace(/([\w\)]){1}\]/g,'$1').replace(/\[([\w]){1}/g,'$1');
console.log(eval(x));
Upvotes: 0
Reputation: 6714
It can be achieved with the usage of negative look aheads and look behinds in regex
let a = "[[[Class1(a1)],[Class2(a2)],[Price(a1,100)]],[[Class3(a3)],[Price(a3,200)]],[]]"
a = a.replace(/(?<!])]/g, "\"")
a = a.replace(/\[(?!\[)/g, "\"")
console.log(JSON.parse(a))
Upvotes: 0
Reputation: 64657
It seems like you can write a pretty straightforward parser:
const parse = (str) => {
let depth = 0;
let item = '';
let items = [];
for (let i = 0; i < str.length; i++) {
if (str[i] === '[') {
depth++;
if (depth === 2) {
items.push([]);
}
}
else if (str[i] === ']') {
if (depth === 3) {
items[items.length - 1].push(item);
item = '';
}
depth--;
}
else if (depth === 3) {
item += str[i]
}
}
return items;
}
console.log(parse("[[[Class1(a1)],[Class2(a2)],[Price(a1,100)]],[[Class3(a3)],[Price(a3,200)]],[]]"));
console.log(parse("[[],[[Class1(a1)],[Color(a1,200)]],[[IsLight(a1,0)]]]"))
Upvotes: 3