保持observableArrays在推送后排序

时间:2018-06-11 15:09:55

标签: javascript algorithm sorting knockout.js

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

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

3 个答案:

答案 0 :(得分:2)

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

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

您只需要定义已排序的inject函数,因为knockout可观察数组已经有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>

答案 1 :(得分:2)

我会考虑两种方法。

  1. 始终对数据进行排序。不要调用.sort(),而是搜索放置元素的正确位置,然后调用.splice()将其插入正确的位置。这是一个O(n)算法,但在实践中应该很快。
  2. 使用https://libraries.io/npm/dsjslib之类的内容始终维护已排序的数据结构。这使得插入/删除O(log(n))操作。但是现在每个操作都有额外的复杂性。
  3. 使用哪一个取决于您的操作是由插入/删除工作,还是通过运行列表并显示它来控制。我最好的猜测是,遍历列表并显示它更重要。

    此外,下一个问题是通过扫描数组或二进制搜索来进行搜索是否更好。扫描是O(n)但是分支预测错误的成本太高,以至于我发现它比二进制搜索更快地插入到数百个元素的列表中。

答案 2 :(得分:2)

使用knockout,你也可以根据你的observable数组创建计算器,所以你总是会有排序的数组

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