我正在尝试根据接近用户位置使用地理点排序可观察数组。我有一个函数,它循环遍历数组中的所有存储并找到离用户当前位置最近的标记。然后我将它嵌套到另一个循环中,使用插入排序对所有元素进行排序。
我有两个问题。第一。我的交换方法有点古怪。我认为它打破了世界。我想我不知道如何在knockout中正确地交换元素。
。这是正确的方法吗?Ko可观察数组有一个内置的排序方法,但我不确定如何使用最接近用户函数的点来实现它。我附上了下面的代码。任何帮助或见解将不胜感激。
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;
几点说明:
当在一个可观察数组上执行大量操作时,最好是在底层数组上执行,然后在完成时向可观察数组发出信号。这将减少对数组的扰动。
下面是一个"更好"的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
方法来比较这个距离属性来加快速度。这样可以避免不断地重新计算距离。