【问题标题】:Combining Adjacent Lines into a single array将相邻行组合成一个数组
【发布时间】:2017-12-05 13:37:39
【问题描述】:

我有一个包含一些 Javascript 对象的数组。每个对象实际上是从画布元素中拉出的水平线段。每个对象都有属性“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; } }
    }

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

谢谢!

【问题讨论】:

  • 这一点都不清楚;此刻发生了什么?有什么问题?
  • 请注明需要的输入json和输出json
  • @user93 请看最后两个代码sn-ps。这是循环之前变量的内容,然后是循环之后的内容。 JSON 并没有真正改变。只是数组被重组
  • 然后删除除数组转换和代码 sn-p 之外的问题的其他部分,因为它不相关
  • @RachelGallen 目前,相邻的线段没有合并到同一个数组中。有些段应该在同一个数组中,但不在循环之后。

标签: javascript arrays algorithm html5-canvas


【解决方案1】:

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

解决方法如下;

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

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

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

所以这很简单。如果你想对所有数组项求和,你可能会喜欢

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

array destructuring 会将第一个数组项(头部)分配给 x 变量,而数组的其余部分(尾部)将作为 xs 数组变量分配给 ...xs rest parameters。换句话说,如果我用[1,2,3,4] 提供sum 函数,在第一轮x 是1 和xs 是[2,3,4]。所以你应该能够遵循递归的其余部分。

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

主函数是getAdjacents,它有两个实用助手函数

  1. isAdjacent:它需要两个段对象并检查它们是否相邻。返回布尔值。
  2. groupBy:这个将已经相邻的线段数组作为第一个参数(as)和一个未经检查的数组数组,每个数组都有一个段对象(rowarray 的尾部最初)作为[b,...bs]。它会返回一个相邻和不相邻的元组,如[[{adj. segment}{adj. segment}...{adj. segment}],[[{segment}],[{segment}],...,[{segment}]]]。

您应该能够继续使用被注释的主要功能的主体。

【讨论】:

  • 这正是我所需要的!非常感谢!
  • 嘿——如果不是太麻烦的话,你能添加几个 cmets 指定每行在做什么以及背后的逻辑吗?
  • @DanD 抱歉,只是可以抽出一些时间来处理它。我稍微修改了一下。现在看起来很扎实。我还在我的答案和代码中添加了解释。随时询问您是否遇到了什么问题。
【解决方案2】:

您可以通过使用简单的过滤器和映射方法来实现结果

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)

【讨论】:

  • 这适用于我编写的示例数组,但这只是一个示例。对于我的特定场景,rowarray 的内容是用户生成的,因此循环需要能够处理任意数量的组(并且不能基于特定的 x 和 y 值进行过滤,因为 x 和 y 值可以是任何东西)。我完全是自学成才,边走边学,所以如果你也能提供一些关于它是如何工作的 cmets,那将会很有用。
  • 您可以根据需要动态替换已赋值的132,146,138,140。
  • 如果您将组作为对象数组,那么您可以通过它进行映射并应用上述代码
  • 现在它将适用于组数组中的任意组。
  • 我很欣赏你的努力,但重点是让算法弄清楚组是什么。转换数组没有问题——这很容易。但是算法需要根据行是否相邻来确定如何转换数组。
【解决方案3】:

您没有明确指定 - 哪些段是“相邻的”,但似乎您需要类似 connected-component labeling 的东西(使用段而不是单元格)并且需要实现一种 union-find algorithm,使 disjoint set data structure。

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

【讨论】:

    猜你喜欢
    • 2023-04-09
    • 2021-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-13
    • 2019-03-15
    相关资源
    最近更新 更多