如何确定Javascript中的项目网格中选择范围之间的重叠

时间:2018-08-30 15:47:44

标签: javascript algorithm range normalization

想象一下类似物品网格的东西。 用户可以在网格中选择多个范围的项目。当有多个范围(选择)时,我想确定最后创建的范围是否与现有范围重叠。 可以将网格系统与文本区域中的字符进行比较,在那里可以突出显示多个文本范围。

  • 每个单个范围始终存在相邻的项目,每个项目旁边 其他,或网格中下一行的第一项。 (也完全是 类似于在文本文档中选择文本。)
  • 用户创建范围时,它可以与现有范围重叠,甚至完全适合现有范围。

范围存储为:

{
    'rangeStartLineIndex': 2,
    'rangeStartColIndex': 9,
    'rangeEndLineIndex': 4,
    'rangeEndColIndex': 7
}

以上范围可以在图像中显示。但是请注意,网格的行数和列数不是恒定的。

enter image description here

目标是遍历现有范围,并查看刚创建的范围是否与现有范围重叠(或完全适合)。如果是这样,则采用该现有范围,并对其进行扩展,以使创建的范围与与其重叠的范围合并。因此,这是一种规范化数据。

另一个代码示例:

var ranges = []; // stores the range objects that are created earlier.
var createdRange = {...}; // range object just created.

for(var i = 0; i < ranges; i++) {
    var overlap = doesThisOverlap(createdRange, ranges[i]);

    if(overlap) {

        // overlaps, which means we extend the existing range.
        range[i].rangeStartLineIndex = Math.min(range[i].rangeStartLineIndex, createdRange.rangeStartLineIndex);
        range[i].rangeStartColIndex = Math.min(range[i].rangeStartColIndex, createdRange.rangeStartColIndex);
        range[i].rangeEndLineIndex = Math.max(range[i].rangeEndLineIndex, createdRange.rangeEndLineIndex);
        range[i].rangeEndColIndex = Math.max(range[i].rangeEndColIndex, createdRange.rangeEndColIndex);

    } else {
        // means the new range does not extend an existing range, so add it.
        ranges.push(createdRange);
    }
}

function doesThisOverlap(rangeA, rangeB) {
    // ???
}

当尝试实现功能doesThisOverlap时,我最终遇到了过多的if块。我感到困惑,也是因为我感觉找到了一种算法。

我还尝试了将范围起始点的行和col索引相加,以赋予它一个“分数”(并对其端点的行和列索引进行相同的操作)。 然后比较范围之间的起点/终点得分。

2 个答案:

答案 0 :(得分:2)

问题不是真正的2D,如果将范围表示为

,则变得更加容易
{
  rangeStart: 29,
  rangeEnd:48
}

您可以通过计算

转换为该表示形式
lineIndex * COLUMN_NUMBER + columnIndex.

基本上,您应该迭代所有范围并找到min rangeStart和rangeEnd。然后,您可以使用以下方法将结果转换为列/行:

columnIndex = x % COLUMN_NUMBER;
lineIndex = parseInt(x / COLUMN_NUMBER).

答案 1 :(得分:1)

识别createdRange是否与ranges之一重叠的一种方法是给每个range起始索引和结束索引,然后检查{{ 1}}是否与其他范围的索引重叠。

首先,让我们从提高可读性的角度更改createdRange对象的形状:

range

将此{ start: { row: 2, col: 9 }, end: { row: 4, col: 7 } } 对象与您定义的对象的映射很简单:

range


顺便说一句,我首先要指出逻辑上的一个小错误。
rangeStartLineIndex => start.row rangeStartColIndex => start.col rangeEndLineIndex => end.row rangeEndColIndex => end.col 循环中,您正在检查for是否与当前createdRange重叠。如果不是,则您要将该range 添加到Ranges数组。
但是,您只需要在createdRange中添加createdRange 如果没有一个范围与ranges

重叠

因此,正确的createdRange循环如下所示:

for

好的,现在让我们看看如何计算给定范围的索引。
如果我们开始在网格中从左到右分配索引(从0开始), 简单的数学说,var hasOverlap = false; // this will tell if any of the ranges overlap for(var i = 0; i < ranges; i++) { var overlap = doesThisOverlap(createdRange, ranges[i]); if(overlap) { // overlaps, which means we extend the existing range. // some logic to update the overlapped ranges hasOverlap = true; // a range has overlapped, set the flag to true break; } } // Did we see any overlap? if(!hasOverlap) { // no we did not, let us add this range to ranges array // means the new range does not extend an existing range, so add it. ranges.push(createdRange); } 行和r列中框的索引为:

c

以下是帮助程序功能,可以帮助计算给定范围内的索引:

index = r * (COL + 1) + c [COL is the total number of columns in the grid]

请注意,function getIndex(row, col, COL) { return row * (COL + 1) + col; } function getIndices(range) { var start = range.start; var end = range.end; var startIndex = getIndex(start.row, start.col, COLS); var endIndex = getIndex(end.row, end.col, COLS); return { start: startIndex, end: endIndex }; } 采用getIndices并输出具有rangestart索引的对象。现在,我们可以计算end和当前createdRange的索引。根据索引,我们将知道范围是否重叠。


问题现在可以归结为:
我们有一条线AB,并给定了新的线PQ,找出新线PQ是否与AB重叠。 (其中A,B,P,Q是数字线上的点,A 拿笔和纸画几条线。您会知道,不会重叠的情况只有两种情况:

  

Q

将此观察结果映射到我们的range对象上,我们可以这样说:

range

这就是代码中的样子:

P => createdRange.startIndex
Q => createdRange.endIndex

A => currentRange.startIndex
B => currentRange.endIndex

请注意,我们摆脱了功能var createdRangeIndices = getIndices(createdRange); var hasOverlap = false; for (var i = 0; i < ranges.length; i++) { var currentRangeIndices = getIndices(ranges[i]); var overlap = (createdRangeIndices.end < currentRangeIndices.start) || (currentRangeIndices.end < createdRangeIndices.start); if (!overlap) { // overlaps, which means we extend the existing range. // some logic to update the overlapped ranges hasOverlap = true; break; } } if (!hasOverlap) { // means the new range does not extend an existing range, so add it. ranges.push(createdRange); } 。一个简单的标志就可以。


如果重叠,现在剩下的就是更新doesThisOverlap的逻辑。 您已经在问题中弄清楚了其中的一部分。我们采用起始索引的最小值和终止索引的最大值。这是该代码:

range

完成!

这是将所有点滴结合在一起的完整代码:

for (var i = 0; i < ranges.length; i++) {
    var currentRangeIndices = getIndices(ranges[i]);
    var overlap = (createdRangeIndices.end < currentRangeIndices.start) 
                    || (currentRangeIndices.end < createdRangeIndices.start);
    if (!overlap) {
        // overlaps, which means we extend the existing range.
        // some logic to update the overlapped ranges

        var start, end;
        if (currentRangeIndices.start < createdRangeIndices.start) {
            start = ranges[i].start;
        } else {
            start = createdRange.start;
        }

        if (currentRangeIndices.end > createdRangeIndices.end) {
            end = ranges[i].end;
        } else {
            end = createdRange.end;
        }
        ranges[i] = { start: start, end: end };
        hasOverlap = true;
        break;
    }
}