如何在JavaScript中将字符串树解析为数组

如何在JavaScript中将字符串树解析为数组,javascript,functional-programming,underscore.js,graph-theory,Javascript,Functional Programming,Underscore.js,Graph Theory,假设我得到一个API响应,它返回一个树状结构,如下所示: "gw43g: (-95.147, 38.5818); " + "jp987h: (" + "bvp7: (-97.450, 30.150); " + "7g8oi: (" + "34ilht: (-82.192997, 29.39719); " + "34hb1: (-122.25, 37.47)); " + "b2

假设我得到一个API响应,它返回一个树状结构,如下所示:

"gw43g: (-95.147, 38.5818); " +
"jp987h: (" +
         "bvp7: (-97.450, 30.150); " +
         "7g8oi: (" +
                 "34ilht: (-82.192997, 29.39719); " +
                 "34hb1: (-122.25, 37.47)); " +
         "b238: (-71.0349, 42.2129)); " +
"ao8yh: (-90.147, 42.5818);"
这是一根绳子

使用JavaScript,我需要将其转换为如下数组

["(-95.147, 38.5818)",
  "(bvp7: (-97.450, 30.150); 7g8oi: (...)...)",
  "(-90.147, 42.5818)"]
…这样,每对括号的内部都是一个数组项,而不管最外层的一对中包含多少嵌套括号

我运气不好,所以我请求你的人帮忙。这是我试过的

function getCoords(str) {
    return str.split(';').map(function(s) {
        return s.substring(s.indexOf('(')+1, s.lastIndexOf(')'));
    });
}

但这是错误的。可以随意调用任何功能工具包(下划线.js等)。不,这不是家庭作业,这是我正在写的一本书。谢谢你的帮助

您可能应该为此使用解析器,但这里有一个类似于小型解析器的快速而肮脏的解决方案:

var src = "gw43g: (-95.147, 38.5818) .... ";

var re = /(\w+:\s*\()|(\);?)|((?:(?!\w+:\s*\(|\);?).)+)/g;

var output = [];
var match;
var stackCount = 0;

while ((match = re.exec(src)))
{
    if (match[1]) {
        if (stackCount == 0) output.push('');
        stackCount++;
    }
    else if (match[2]) {
        stackCount--;
    }
    output[output.length-1] += match[0];
}
console.log(output);
正则表达式将标记分为三类:堆栈开启器、堆栈关闭器或中性。如果它找到一个堆栈起始点,而堆栈上没有任何内容,则会添加一个新的数组项,如果它找到一个更接近的数组项,则会将堆栈向下移动一个。在堆栈为零之前,它将一直追加到当前数组项

我没办法,所以我继续做了,刚刚完成了这个简单的解析器,所以字符串被输出为一个树,其中根属性是键(gw43g),每个都有一个X,Y值,或者它是树上的一个分支

function parseBody(str) { 
    // rey: http://rey.gimenez.biz/s/fxd02f
    var re = /\s+|(\w+)\s*:\s*\(|(\);?)|(([\-+]?\s*(?:\d*\.\d*|\d+))\s*,\s*([\-+]?\s*(?:\d*\.\d*|\d+)))/g;

    var output = [];
    var match;
    var newObj;
    var root = { children: { } }
    var branch = root;

    while ((match = re.exec(str)))
    {
        // key and open
        if (match[1]) {
            newObj = { parent: branch, children: { } };
            branch.children[match[1]] = newObj;
            // new stack
            branch = newObj;
        }
        // 
        else if (match[2]) {
            // move up stack
            branch = branch.parent;
        }
        else if (match[3]) {
            branch.X = parseFloat(match[4]);
            branch.Y = parseFloat(match[5]);
        }
    }

    return root;
}

您可能应该为此使用解析器,但这里有一个类似于小型解析器的快速而肮脏的解决方案:

var src = "gw43g: (-95.147, 38.5818) .... ";

var re = /(\w+:\s*\()|(\);?)|((?:(?!\w+:\s*\(|\);?).)+)/g;

var output = [];
var match;
var stackCount = 0;

while ((match = re.exec(src)))
{
    if (match[1]) {
        if (stackCount == 0) output.push('');
        stackCount++;
    }
    else if (match[2]) {
        stackCount--;
    }
    output[output.length-1] += match[0];
}
console.log(output);
正则表达式将标记分为三类:堆栈开启器、堆栈关闭器或中性。如果它找到一个堆栈起始点,而堆栈上没有任何内容,则会添加一个新的数组项,如果它找到一个更接近的数组项,则会将堆栈向下移动一个。在堆栈为零之前,它将一直追加到当前数组项

我没办法,所以我继续做了,刚刚完成了这个简单的解析器,所以字符串被输出为一个树,其中根属性是键(gw43g),每个都有一个X,Y值,或者它是树上的一个分支

function parseBody(str) { 
    // rey: http://rey.gimenez.biz/s/fxd02f
    var re = /\s+|(\w+)\s*:\s*\(|(\);?)|(([\-+]?\s*(?:\d*\.\d*|\d+))\s*,\s*([\-+]?\s*(?:\d*\.\d*|\d+)))/g;

    var output = [];
    var match;
    var newObj;
    var root = { children: { } }
    var branch = root;

    while ((match = re.exec(str)))
    {
        // key and open
        if (match[1]) {
            newObj = { parent: branch, children: { } };
            branch.children[match[1]] = newObj;
            // new stack
            branch = newObj;
        }
        // 
        else if (match[2]) {
            // move up stack
            branch = branch.parent;
        }
        else if (match[3]) {
            branch.X = parseFloat(match[4]);
            branch.Y = parseFloat(match[5]);
        }
    }

    return root;
}
您可以这样使用:

"gw43g: (-95.147, 38.5818); " +
"jp987h: (" +
         "bvp7: (-97.450, 30.150); " +
         "7g8oi: (" +
                 "34ilht: (-82.192997, 29.39719); " +
                 "34hb1: (-122.25, 37.47)); " +
         "b238: (-71.0349, 42.2129)); " +
"ao8yh: (-90.147, 42.5818);"
var str=“gw43g:(-95.147,38.5818)+
“jp987h:(”+
bvp7:(-97.450,30.150)+
“7g8oi:(”+
34ilht:(-82.192997,29.39719)+
34hb1:(-122.25,37.47))+
b238:(-71.0349,42.2129))+
ao8yh:(-90.147,42.5818)
var cnt=0;//记下打开的括号
var result=Array.prototype.reduce.call(str,function(prev,curr){
如果(curr==='('&&cnt++===0)上推('');
如果(cnt>0)上一个[上一个长度-1]+=curr;
如果(curr==''))cnt--;
返回上一个;
}, []);
控制台日志(结果)您可以这样使用:

"gw43g: (-95.147, 38.5818); " +
"jp987h: (" +
         "bvp7: (-97.450, 30.150); " +
         "7g8oi: (" +
                 "34ilht: (-82.192997, 29.39719); " +
                 "34hb1: (-122.25, 37.47)); " +
         "b238: (-71.0349, 42.2129)); " +
"ao8yh: (-90.147, 42.5818);"
var str=“gw43g:(-95.147,38.5818)+
“jp987h:(”+
bvp7:(-97.450,30.150)+
“7g8oi:(”+
34ilht:(-82.192997,29.39719)+
34hb1:(-122.25,37.47))+
b238:(-71.0349,42.2129))+
ao8yh:(-90.147,42.5818)
var cnt=0;//记下打开的括号
var result=Array.prototype.reduce.call(str,function(prev,curr){
如果(curr==='('&&cnt++===0)上推('');
如果(cnt>0)上一个[上一个长度-1]+=curr;
如果(curr==''))cnt--;
返回上一个;
}, []);

控制台日志(结果)
我做对了:您正在对字符串使用
Array.prototype.reduce
函数,以便它像字符数组一样逐个字符枚举?非常酷。@DanielGimenez:没错!大多数数组方法也适用于“类似数组”的对象,如字符串。看一看:并且看起来像var result=str.split(“”)。reduce((prev,curr)=>{…})也可以工作吗?或者,您可以调用:
Array.from(str)。reduce((prev,curr)=>/*code*/,[])
我很正确:您正在对字符串使用
Array.prototype.reduce
函数,这样它就可以像一个字符数组一样一个字符一个字符地枚举?非常酷。@DanielGimenez:没错!大多数数组方法也适用于“类似数组”的对象,如字符串。看一看:而且似乎var result=str.split(“”)。reduce((prev,curr)=>{…})也可以工作吗?或者,您可以调用:
Array.from(str)。reduce((prev,curr)=>/*code*/,[])
我非常喜欢stewe的答案,它简洁地解决了您描述的问题,但是我继续,在我的答案中添加了一个完全实现的解析器,这样您就可以将数据作为对象而不是字符串数组来获取。[我真的只是想进入你的书:-)丹尼尔,你的答案更长,我希望能有更多的“函数式编程”,但是它快了5倍!我使用JSLitmus进行测试,结果如下:…我希望我能选择两者作为正确答案我真的很喜欢stewe的答案,它简洁地解决了您描述的问题,但我继续在我的答案中添加了一个完全实现的解析器,以便您可以将数据作为对象而不是字符串数组获取。[我真的只是想进入你的书:-)丹尼尔,你的答案更长,我希望能有更多的“函数式编程”,但是它快了5倍!我使用JSLitmus进行测试,结果如下:……我希望我能选择两者作为正确答案