将相邻线组合成单个阵列

时间:2017-07-01 22:47:17

标签: javascript arrays algorithm html5-canvas

我有一个包含一些Javascript对象的数组。每个对象实际上是从canvas元素中提取的水平线段。每个对象都具有属性“lowx”(段的左x坐标),“highx”(段的右x坐标)和“yvalue”(线段占据的y坐标,因为它是水平)。

我需要一个将所有相邻线段组合到同一个数组中的循环。这是一个示例数组转换:

以下是在运行此循环之前rowarray的示例:

var rowarray = [
[{"lowx": 210, "highx": 212, "yvalue": 132}],
[{"lowx": 208, "highx": 214, "yvalue": 133}],
[{"lowx": 207, "highx": 215, "yvalue": 134}],
[{"lowx": 207, "highx": 216, "yvalue": 135}],
[{"lowx": 206, "highx": 216, "yvalue": 136}],
[{"lowx": 206, "highx": 216, "yvalue": 138}],
[{"lowx": 205, "highx": 220, "yvalue": 139}],
[{"lowx": 199, "highx": 209, "yvalue": 140}]
]

在遍历循环之后,rowarray应该包含两个数组(因为有两组相邻的线段)。 y值为132-136的线段位于第一个数组中,y值为138-140的线段应位于第二个数组中。

因此,rowarray看起来像:

var rowarray = [
[{"lowx": 210, "highx": 212, "yvalue": 132},
{"lowx": 208, "highx": 214, "yvalue": 133},
{"lowx": 207, "highx": 215, "yvalue": 134},
{"lowx": 207, "highx": 216, "yvalue": 135},
{"lowx": 206, "highx": 216, "yvalue": 136}],

[{"lowx": 206, "highx": 216, "yvalue": 138},
{"lowx": 205, "highx": 220, "yvalue": 139},
{"lowx": 199, "highx": 209, "yvalue": 140}]

]

这是我所拥有的代码,我认为它已接近工作,但尚未完成。通常它的rowarray最终会成为完全不合逻辑的东西。有空对象(不知道为什么?)和应该放在同一个数组中的线段不是。

由于项目的性质,这需要在客户端javascript中完成。但是,使用JQuery很好并受到欢迎。

    var keepgoing = 1;
    var errorcounter = 0;
    while (keepgoing == 1) {
        try {
            hadtocombine = 0;
            for (var x = rowarray.length-1; x >= 0; x--) {
                for (var w = rowarray.length-1; w >= 0; w--) {
                    for (var q = rowarray[x].length-1; q >= 0; q--) {
                            for (var z = rowarray[w].length-1; z >= 0; z--) {
                                if ((rowarray[x][q].yvalue == (rowarray[w][z].yvalue + 1)) || (rowarray[x][q].yvalue == (rowarray[w][z].yvalue - 1))) {
                                    if ((rowarray[x][q].highx >= rowarray[w][z].lowx) && (rowarray[x][q].highx <= rowarray[w][z].highx)) {
                                        rowarray.splice(w,1);
                                        rowarray.splice(x,1);
                                        rowarray.push(rowarray[x].concat(rowarray[w]));
                                        hadtocombine = 1;
                                    }
                                    else if ((rowarray[x][q].lowx >= rowarray[w][z].lowx) && (rowarray[x][q].lowx <= rowarray[w][z].highx)) {
                                        rowarray.splice(w,1);
                                        rowarray.splice(x,1);
                                        rowarray.push(rowarray[x].concat(rowarray[w]));
                                        hadtocombine = 1;
                                    }
                                    else if ((rowarray[x][q].highx >= rowarray[w][z].highx) && (rowarray[x][q].lowx <= rowarray[w][z].lowx)) {
                                        rowarray.splice(w,1);
                                        rowarray.splice(x,1);
                                        rowarray.push(rowarray[x].concat(rowarray[w]));
                                        hadtocombine = 1;
                                    }
                                    else if ((rowarray[x][q].lowx >= rowarray[w][z].lowx) && (rowarray[x][q].highx <= rowarray[x][q].highx)) {
                                        rowarray.splice(w,1);
                                        rowarray.splice(x,1);
                                        rowarray.push(rowarray[x].concat(rowarray[w]));
                                        hadtocombine = 1;
                                    }
                                }
                            }
                    }
                }
            }
            if (hadtocombine == 0) { 
                keepgoing = 0;
            }
        } catch (err) { errorcounter++; if(errorcounter >= 20000) { keepgoing = 10; } }
    }

希望我能够很好地解释这一点 - 如果没有评论,我会修改。这是我的第一篇文章(经过多年的潜伏),所以我希望我已经做好了。

谢谢!

3 个答案:

答案 0 :(得分:1)

您可以使用简单的过滤器和地图方法

来实现结果

var rowarray = [
  [{
    "lowx": 210,
    "highx": 212,
    "yvalue": 132
  }],
  [{
    "lowx": 208,
    "highx": 214,
    "yvalue": 133
  }],
  [{
    "lowx": 207,
    "highx": 215,
    "yvalue": 134
  }],
  [{
    "lowx": 207,
    "highx": 216,
    "yvalue": 135
  }],
  [{
    "lowx": 206,
    "highx": 216,
    "yvalue": 136
  }],
  [{
    "lowx": 206,
    "highx": 216,
    "yvalue": 138
  }],
  [{
    "lowx": 205,
    "highx": 220,
    "yvalue": 139
  }],
  [{
    "lowx": 199,
    "highx": 209,
    "yvalue": 140
  }]
]

const group = [{
    l: 132,
    h: 136
  },
  {
    l: 138,
    h: 140
  }
];

rowarray = rowarray.map(x => x[0])

const limit = (h, l, x) => x.yvalue <= h || x.yvalue <= l
let gg = [];
group.map(g => {
  gg.push(rowarray.filter(x => limit(g.l, g.h, x)))
});


console.log(gg)

答案 1 :(得分:1)

您没有明确说明 - 哪些细分是&#34;相邻&#34;,但似乎您需要类似connected-component labeling的内容(使用细分而不是细胞)并且需要实现一种{{3} },制作union-find algorithm

此问题也可以被视为计算几何中的矩形联合问题(通过联合查找和/或扫描线方法求解)

答案 2 :(得分:1)

对于以下代码,如果两个段在x轴上相交并且它们在相邻或相同的y值上,则它们是相邻的。

解决方案如下;

&#13;
&#13;
function getAdjacents([x,...xs], r = []){ // x is the first item and xs is the rest of the input array.

  function isAdjacent(a,b){
    return (a.lowx < b.highx && b.lowx < a.highx) && Math.abs(a.yvalue - b.yvalue) < 2;
  }
  
  function groupBy(as, [b,...bs], r = [[],[]]){
    as.some(a => isAdjacent(a,b[0])) ? r[0].push(b[0]) : r[1].push(b);
    return bs.length ? groupBy(as.concat(r[0]), bs, r)
                     : r;
  }
  
  var [as,bs] = xs.length ? groupBy(x,xs) : [[],[]],              // group the rest according to the first element
      cs      = x.concat(as);                                     // first element and it's adjacent elements
  
  return bs.length ? as.length ? getAdjacents([cs].concat(bs),r)  // if adjacent elements are found then keep checking
                               : (r.push(cs), getAdjacents(bs,r)) // if not push previous adjacents into result and start with the remaining
                   : (r.push(cs), r);                             // if no more remaining return result
}

var rowarray = [[{"lowx": 210, "highx": 212, "yvalue": 132}],
                [{"lowx": 208, "highx": 214, "yvalue": 133}],
                [{"lowx": 207, "highx": 215, "yvalue": 134}],
                [{"lowx": 207, "highx": 216, "yvalue": 135}],
                [{"lowx": 206, "highx": 216, "yvalue": 136}],
                [{"lowx": 206, "highx": 216, "yvalue": 138}],
                [{"lowx": 205, "highx": 220, "yvalue": 139}],
                [{"lowx": 199, "highx": 209, "yvalue": 140}]
               ];
               
console.log(getAdjacents(rowarray));
&#13;
&#13;
&#13;

所以我不得不修改我的代码,因为当输入数组被洗牌时它总是没有返回相同的答案。现在好了。

我在这里使用一种非常不寻常的模式进行递归迭代。如果您了解Haskell,那么以(x:xs)形式表达数组是非常基本的知识。 ES6也允许在JS中进行类似的模式匹配。我很有趣,直到我使用它,我没有在任何书籍或博客中看到它。

所以它非常简单。如果你想要总结你可能喜欢的所有数组项目

function sum([x,...xs]){
  return xs.length ? x + sum(xs) : x;
}

array destructuring会将第一个数组项(head)分配给x变量,数组的其余(尾部)将分配给...xs rest parameters作为xs数组变量。换句话说,如果我使用sum[1,2,3,4]函数提供信息,则第一个回合x1xs[2,3,4]。所以你应该能够跟随递归的其余部分。

现在在这个答案中,我经常使用这种模式。但是既然现在你知道它是什么,你可以简单地遵循逻辑。

主要功能是getAdjacents,它有两个实用助手功能

  1. isAdjacent:它需要两个段对象并检查它们是否相邻。返回布尔值。
  2. groupBy:这个作为第一个参数(即as)已经相邻的一系列线段和一个未经检查的数组数组,每个数组都有一个段对象( rowarray最初的尾部)为[b,...bs]。它会返回一个相邻的元组,而不是像[[{adj. segment}{adj. segment}...{adj. segment}],[[{segment}],[{segment}],...,[{segment}]]]那样相邻。
  3. 任何你应该能够继续使用被评论的主要功能的主体。