Knockout observable array按地理位置排序。

时间:2013-05-28 10:11:54

标签: sorting knockout.js geopoints knockout-sortable

我正在尝试根据与用户位置的接近程度使用地理点对淘汰可观察数组进行排序。我有一个函数循环遍历我的数组中的所有商店,并找到最接近用户当前位置的标记。然后我将其嵌套在另一个循环中,使用插入排序对所有元素进行排序。

我有两个问题。第一。我的交换方法有点时髦。我认为它打破了dom。不要以为我理解如何在淘汰赛观察中正确地交换元素。

二。这甚至是正确的方法吗? Ko observable数组有一个内置的排序方法,但我不知道如何使用最接近用户函数的点来实现它。我附上了下面的代码。任何帮助或见解将不胜感激。

var stores = ko.observableArray(); storesRepository.getFeed(stores);

function closestMarker(lat, lng) {

            var pi = Math.PI;
            var R = 6371; //equatorial radius
            var lat1 = lat;
            var lon1 = lng;

            var distances, closest, min, chLat, chLon, dLat, dLon, rLat1, rLat2, a, c, d;

            for (j = 0; j < stores().length; j++) { // outer loop uses insertion sort to "sort" elements.
                 distances = [];
                 closest = -1;
                 min = 0;
                for (i = j+1; i < stores().length; i++) { // inner loop finds closest marker to user

                    var lat2 = stores()[i].latitude();
                    var lon2 = stores()[i].longitude();
                     chLat = lat2 - lat1;
                     chLon = lon2 - lon1;
                     dLat = chLat * (pi / 180);
                     dLon = chLon * (pi / 180);

                     rLat1 = lat1 * (pi / 180);
                    rLat2 = lat2 * (pi / 180);

                     a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                            Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(rLat1) * Math.cos(rLat2);
                     c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                     d = R * c;

                    distances[i] = d;
                    if (closest == -1 || d < distances[closest]) {
                        closest = i;
                    }


                }
                swap(j, closest);

            }
            function swap(a, b) { // i dont think this is the right approach
               // alert("working");
               var temp = stores()[b];
               stores.replace(stores()[b],stores()[a]);
               stores.replace(stores()[a], temp);

            }

        }
        return stores;

1 个答案:

答案 0 :(得分:0)

有几点:

当对可观察数组执行大量操作时,最好在底层数组上执行它们,然后在完成后发出可观察数组的信号。这将减少阵列上的流失。

以下是遵循上述规则的“更好”的swap实现:

function swap(a, b) {
    var ary = stores(),
        temp = ary[a];

    stores.valueWillMutate();
    ary[a] = ary[b];
    ary[b] = temp;
    stores.valueHasMutated();
}

现在我已经说过了,你不应该使用它:)你应该尝试使用内置的sort函数而不是使用插入排序。这通过第一条规则更好地通过仅在排序操作完成后发送通知而不是在每次交换时发送通知。内置排序使用本机浏览器代码,可能比您编写的JavaScript代码更快。您需要做的就是编写一个比较函数,指示a或b是否更接近用户,然后将其传递给sort方法:

var distanceToUser = function (a) {
    var lat2 = a.latitude(),
        lon2 = a.longitude(),
        chLat = lat2 - lat1,
        chLon = lon2 - lon1,
        dLat = chLat * pi / 180,
        dLon = chLon * pi / 180,
        rLat1 = lat1 * pi / 180,
        rLat2 = lat2 * pi / 180,
        aa = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
             Math.sin(dLon / 2) * Math.sin(dLon / 2) *
             Math.cos(rLat1) * Math.cos(rLat2),
        c = 2 * Math.atan2(Math.sqrt(aa), Math.sqrt(1 - aa));

    return R * c;
},
compare = function (a, b) {
    var da = distanceToUser(a),
        db = distanceToUser(b);

    return da < db ? -1 : (da > db ? 1 : 0);
};

stores.sort(compare);

如果你的stores数组中有大量项目,那么可以通过循环数组一次来计算到用户的距离并将其作为属性存储在每个项目中来加速数组,然后更改compare方法只是比较这个距离属性。这样可以防止不断重复计算距离。