简体   繁体   中英

More performant way of sorting by selected first

I have a table and a map above that contains markers that relate to the table rows below. When a user selects items on the map above it highlights the matching rows then moves those rows to the top of the table but sorts by the user's sorting setting.

For example, if you had this raw data (not displayed data):

[
  {id: 1, name: 'Z'},
  {id: 2, name: 'Y'},
  {id: 3, name: 'X'},
  {id: 4, name: 'W'}
]

Then the user selects id: 1 and id: 3 on the map and has it sorted by name . The data becomes:

[
  {id: 3, name: 'X'}, // selected
  {id: 1, name: 'Z'}, // selected
  {id: 4, name: 'W'}, // not selected
  {id: 2, name: 'Y'}  // not selected
]

There's some data sets with 10,000+ rows and I'm worried this is locking up too long when they select the items because of the 2 filters + concat. Is there a way to do this in one pass or just a way to prevent the browser from locking while it's doing this?

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

Try reduce. In the callback function, it checks for whether the row was selected or not and pushes it to the appropriate array in the result object. You do this in one pass and then concat the arrays in the last step.

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

I'm also a big fan of lodash and here's the lodash implementation. I first pass the array to the chain function which basically wraps it around a lodash wrapper which enables chaining of lodash functions. I then pass the array through my reduce function, similar to what I did above with Array.prototype.reduce , but this time the result's initial value is an array of two arrays: the first array is meant for selected rows while the second is for non-selected rows. I then use _.flatten to flatten the nested array so now we have an array of objects instead of an array of arrays. I call value to unwrap the lodash wrapper and get my finally value which is an array of rows.

/**
 * 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();

Rather than re-sorting every time the user updates, keep two persistent arrays. One for selected items, and one for items that are not selected. When a user selects an item, splice it out of the not selected items array, and push it into the selected items array. Then update the final array by concatenating those two arrays.

You might end up with something along these lines:

  var selectedItem = nonSelectedItems.splice(selectedItemIndex, 1);

  selectedItems.push(selectedItem);

  var allRows = selectedRows.concat(nonSelectedRows);

  myTable.updateRowSort(allRows);

...where selectedItems and nonSelectedItems are arrays that live outside of the scope of whatever event handler is called to update the order when a user selects something.

To keep original index you will need to add .index property to each record:

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

And then sorting is trivial:

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

Use one more variable selected-index = 0 - increment on each select. Use unshift to push @top filter or use splice to push @ selected-index , if u want to use the sorted position iterate 0 to selected-index &push using splice

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM