【问题标题】:keep observableArrays sorted after push推送后保持 observableArrays 排序
【发布时间】:2018-06-11 15:09:55
【问题描述】:

我有两个可观察数组,我需要从第一个数组中删除元素并推送到第二个数组,反之亦然。但是当我这样做时,字母排序就搞砸了。

self.allCourses = ko.observableArray([]);
self.selectedCourses = ko.observableArray([]);

我将在两个数组之间交换课程,并使用这个:

self.sortArrays = function(){
    self.allCourses.sort(function (l, r) {
        return  l.code() < r.code() ;
    });
    self.selectedCourses.sort(function (l, r) {
        return  l.code() < r.code() ;
    });
}

不仅效率不高,而且没有按预期工作;我每次调用这些函数之一时都会调用该函数

self.addCourse = function(course){
    self.selectedCourses.push(course);
    self.allCourses.remove(course);
     self.sortArrays();
};
self.removeCourse = function(course){
    self.allCourses.push(course);
    self.selectedCourses.remove(course);
     self.sortArrays();
};

【问题讨论】:

  • 为什么在添加时删除课程,反之亦然?
  • sort 函数中返回&lt; 永远不会按预期工作。您应该返回一个正值、0 或负值。
  • 这些是专业,所以我实际上是在填写专业的课程,反之亦然,将它们从专业中删除
  • @Isac 这可能是一个很好的注意事项,但即使这个有效,它仍然很耗时,尤其是在有很多课程时

标签: javascript algorithm sorting knockout.js


【解决方案1】:

从数组中删除一个项目时,您永远不必重新排序。

您可以使用排序定义插入一个项目,而不是 推送 和重新排序。

您只需要定义排序注入函数,因为可淘汰的可观察数组已经有一个remove 方法:

const sorter = (a, b) => a > b ? 1 : a < b ? -1 : 0;

const leftNumbers = ko.observableArray(
  [3,5,1,2].sort(sorter)
);
const rightNumbers = ko.observableArray(
  [4,1,3,5].sort(sorter)
);

// There are many ways to write this function, which you can probable
// find on stack overflow. The destructuring probably makes this slower
// than just re-sorting. I'll leave it up to you to optimize for performance.
const injectSorted = (sorter, arr, nr) => {
  const pos = arr.findIndex(x => sorter(x, nr) > -1);

  if (pos === -1) return arr.concat(nr);
  
  return [
    ...arr.slice(0, pos),
    nr,
    ...arr.slice(pos)
  ];
};

// Notice how we don't need to re-sort
const moveFromTo = (arr1, arr2) => x => {
  arr2(injectSorted(sorter, arr2(), arr1.remove(x)));
};

ko.applyBindings({ leftNumbers, rightNumbers, moveFromTo });
div { display: flex; justify-content: space-around; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<p>Click numbers to move between lists</p>
<div>
  <ul data-bind="foreach: leftNumbers">
    <li data-bind="click: moveFromTo(leftNumbers, rightNumbers), text: $data"></li>
  </ul>
  <ul data-bind="foreach: rightNumbers">
    <li data-bind="click: moveFromTo(rightNumbers, leftNumbers), text: $data"></li>
  </ul>
</div>

【讨论】:

    【解决方案2】:

    我会考虑两种方法。

    1. 始终保持数据排序。不要调用.sort(),而是搜索放置元素的正确位置,然后调用.splice() 将其插入正确的位置。这是一个O(n) 算法,但在实践中应该很快。
    2. 使用https://libraries.io/npm/dsjslib 之类的东西随时维护排序的数据结构。这使得插入/删除成为O(log(n)) 操作。但是,现在每个操作都具有额外的复杂性。

    使用哪一个取决于您的操作主要是插入/删除,还是遍历列表并显示它。我的最佳猜测是遍历列表并显示它更重要。

    此外,下一个问题是通过扫描数组进行搜索还是通过二分查找更好。扫描是O(n),但分支预测错误的代价如此之大,以至于我发现它比二进制搜索插入数百个元素的列表要快。

    【讨论】:

      【解决方案3】:

      使用淘汰赛,你还可以根据你的可观察数组创建计算,所以你总是会有排序数组

      self.allCoursesSorted = ko.computed(function(){
          return this.allCourses.sort(function (l, r) {
             return  l.code() < r.code() ;
          });
      }, this); 
      

      对于选定的课程,您可以使用相同的方法,但使用过滤器

      self.allCoursesSelected = ko.computed(function(){
          return ko.utils.arrayFilter(this.allCoursesSorted(), 
              function (item) {
                  return item.selected === true;
              });
      }, this); 
      

      【讨论】:

        猜你喜欢
        • 2019-06-13
        • 1970-01-01
        • 2023-01-07
        • 1970-01-01
        • 2017-07-03
        • 2011-11-19
        • 1970-01-01
        • 2018-02-11
        • 2015-02-10
        相关资源
        最近更新 更多