4

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.parsedoesn't work either.

Is there a way to actually convert this string to an array in Javascript?

4
  • 6
    You might need to write a parser for this. There isn't a generic way to do it and using regexes is going to be hacky and hard to maintain. Commented Jul 23, 2020 at 7:02
  • 1
    Class1(a1) is a function or just a string? Commented Jul 23, 2020 at 7:06
  • The string is already valid JavaScript, so you can just use an existing JS parser (e.g. Babel) to parse it. Commented Jul 23, 2020 at 7:28
  • just a string, I need the elements as strings in the array. Commented Jul 23, 2020 at 9:42

4 Answers 4

3

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)]]]"))

Sign up to request clarification or add additional context in comments.

2 Comments

Can you explain your thought process, please? Thank you. Is this more efficient than regex?
+1 Your parser is much faster than my regex-based approach: jsperf.com/https-stackoverflow-com-questions-63048607
1

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:

  1. A quotation mark is put before every character that is not a bracket, but follows an opening bracket (checked using positive lookbehind).
  2. A quotation mark is put after every character that is not a bracket, but precedes a closing bracket (checked using positive lookahead).

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

2 Comments

A clever solution, I didn't think about that. How would evaluate the performance between your regex and Dave's parser? Which one would be more efficient?
@JackHu Dave's version is much faster: jsperf.com/https-stackoverflow-com-questions-63048607
0

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))

3 Comments

You don't need negative lookaheads or lookbehinds, you can just do a = a.replace(/\[([^\[\]]+)\]/g, '"$1"')
i know it's really not necessary, but it's still another approach, that could be useful nonetheless
Keep in mind your approach converts the empty inner array in index 2 to an empty string even though it shouldn't. Mine doesn't do that.
0

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));

3 Comments

Eval is unsafe. In my answer I suggested a safe approach.
Eval is actually kinda ok when in browser, everyone can inject any code to any site with inspector or an extension, so wouldn't consider it as harmful, but did warn the author. If it's nodejs or similar, can be actually really dangerous if the rest is not coded correctly.
Imagine that the above string comes from a database, and an evil attacker could replace one of the above function calls with code that changes the site to do something harmful to the user (for example, to steal their data from forms when the submit button is clicked). Eval won't stop these malicious scripts from doing whatever they want with the page, but if the code is executed in the context of a Web Worker (see the link in my answer), then it won't have access to the page.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.