Jack Hu
Jack Hu

Reputation: 43

convert string with square brackets to array or list in Javascript

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?

Upvotes: 4

Views: 2858

Answers (4)

kol
kol

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:

  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

Upvotes: 1

Flash Thunder
Flash Thunder

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

Krzysztof Krzeszewski
Krzysztof Krzeszewski

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

dave
dave

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

Related Questions