通过选择排序的更高效方法

3
我是一个有用的助手,可以翻译文本。
上方有一个包含标记的地图和一个表格,这些标记与下方表格行相关。当用户在上方的地图上选择项目时,它会突出显示匹配的行,然后将这些行移动到表格顶部,并按用户的排序设置进行排序。
例如,如果您有以下“原始”数据(未显示数据):
[
  {id: 1, name: 'Z'},
  {id: 2, name: 'Y'},
  {id: 3, name: 'X'},
  {id: 4, name: 'W'}
]

然后用户在地图上选择id:1id:3,并按name排序。数据变为:
[
  {id: 3, name: 'X'}, // selected
  {id: 1, name: 'Z'}, // selected
  {id: 4, name: 'W'}, // not selected
  {id: 2, name: 'Y'}  // not selected
]

有一些数据集有10,000+行,我担心当他们选择项目时,由于2个过滤器+连接,这会锁定太长时间。有没有一种可以一次完成此操作的方法,或者只是一种可以在执行此操作时防止浏览器锁定的方法?
  var selectedRows = rows.filter(function (row) {
    return selectedMarkerIds.indexOf(row.id) !== -1
  });

  var nonSelectedRows = rows.filter(function (row) {
    return selectedMarkerIds.indexOf(row.id) == -1
  });

  var allRows = selectedRows.concat(nonSelectedRows);

  myTable.updateRowSort(allRows);

为什么不直接对原始数组进行排序呢? - c-smile
如何实现?rows是一个数组。我尝试了很多比较方法,但我无法弄清楚如何保持原始排序顺序,只将选定的行提升到顶部。 - Oscar Godson
也许可以添加一个布尔键,然后重新定义JavaScript的“sort”函数。 - Seblor
你正在使用分页吗?理想情况下,如果需要的话,你应该操作所有的10,000行,但只对可见元素引入DOM操作。DOM操作和相关的重绘是会成为你应用程序瓶颈的地方。 - code_monk
@code_monk 没有真正考虑过,但那可能会大大加快速度。 - Oscar Godson
显示剩余2条评论
4个回答

1
尝试使用reduce。在回调函数中,检查行是否被选择,并将其推送到结果对象中的适当数组中。您可以在一次遍历中完成此操作,然后在最后一步连接数组。
var result = rows.reduce((result, row) => {
  selectedMarkerIds.indexOf(row.id) !== -1 ? result.selected.push(row) : result.notSelected.push(row);
  return result;
}, {
  selected: [],
  notSelected: []
});

result.selected.concat(result.notSelected);

我也是lodash的忠实粉丝,这里是lodash的实现方式。我首先将数组传递给chain函数,它基本上将其包装在一个lodash包装器中,从而使lodash函数可以链接。然后,我通过我的reduce函数将数组传递,类似于我以上使用Array.prototype.reduce所做的操作,但这次结果的初始值是两个数组的数组:第一个数组用于选择的行,而第二个数组用于未选择的行。然后,我使用_.flatten来展开嵌套数组,因此现在我们有了一个对象数组而不是一个数组的数组。我调用value来取消lodash包装器并获得最终值,即行数组。

/**
 * in the reduce function, the initial value of the 
 * result is an array of two arrays. 
 * the first array is for selected items.
 * the second array is for non-selected items.
 */
var newArray = _.chain(rows)
  .reduce((result, row) => {
    selectedMarkerIds.indexOf(row.id) !== -1 ? result[0].push(row) : result[1].push(row);
    return result;
  }, [ [], [] ])
  .flatten()
  .value();

1
你的 reduce 看起来快了大约 2.5 倍。在一个有 12K 行数据集上运行 reduce,相比于两个过滤器,只需要大约 3.6 毫秒。谢谢! - Oscar Godson

0
与其每次用户更新时重新排序,不如保留两个持久数组。一个用于选定的项目,另一个用于未选定的项目。当用户选择一个项目时,将其从未选定的项目数组中拼接出来,并将其推入选定的项目数组中。然后通过连接这两个数组来更新最终数组。
你可能会得到类似以下的东西:
  var selectedItem = nonSelectedItems.splice(selectedItemIndex, 1);

  selectedItems.push(selectedItem);

  var allRows = selectedRows.concat(nonSelectedRows);

  myTable.updateRowSort(allRows);

...其中selectedItems和nonSelectedItems是在用户选择某些内容时,更新订单的事件处理程序范围之外存在的数组。


0

为了保留原始索引,您需要为每个记录添加 .index 属性:

var indexed = rs.map( function(el,index) { el.index = index; return el; } );

然后排序就很简单了:

var sorted = indexed.sort(function(a,b) {
  var asel = selectedMarkerIds.indexOf(a.id) !== -1;
  var bsel = selectedMarkerIds.indexOf(b.id) !== -1; 

  if( asel && bsel )
    return a.name < b.name ? -1:1;

  if( asel ) return -1;
  if( bsel ) return +1;

  // both records are in not selected set
  return a.index - b.index;
});

我需要在排序更改时(搜索、列标题单击等)更新该索引,对吗? - Oscar Godson
不,该索引维护原始的“未排序”顺序。当selectedMarkerIds更改时,您只需要调用indexed.sort部分即可。 - c-smile
啊,好的。用户可以按照他们想要的方式进行排序/筛选,所以在我上面的示例中是按照“名称”列排序的。默认情况下,它只是按照添加到数据库中的索引排序(第一个代码块)。它显示出来后,他们点击“名称”列标题。现在它按照“名称”排序。当他们选择标记时,它需要按照“名称”(按字母顺序)分为两个类别:已选择和未选择(第二个代码块)。我认为你的解决方案可以工作,但地图需要在每次排序和筛选时发生。 - Oscar Godson
该比较函数按照名称字母顺序对选定的组进行排序。未选择的组不会排序 - 其中的记录保持原始顺序。 - c-smile
为了使第一组非排序修改比较器,请使用以下代码: if( asel && bsel ) return a.index - b.index; - c-smile

0

使用一个额外的变量selected-index = 0 - 每次选择时递增。使用unshift将@top过滤器推送或使用splice将@ selected-index推送,如果您想使用排序位置迭代0到selected-index并使用splice进行推送。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接