按相同顺序对两个单独的列表进行排序

本文关键字:单独 两个 列表 排序 顺序 | 更新日期: 2023-09-27 18:20:58

只是先制定一些限制。由于我编写代码的环境,我无法创建自己的类或方法。只是基本的程序代码。它在CMS中,我的代码在方法本身中执行。

这是我的问题

在这个页面上,我做了一个数据库查询,加载了所有700多个商店的位置。然后,我根据查询字符串中的lat和lng值与数据库中的值进行距离计算,以查找50公里内的商店。在这个距离内的每一个,我当前都添加到List<DataRow>中。我还获取距离结果,将其四舍五入到小数点后一位,并将其存储到Double类型的简单列表中。我想做的,基本上是对这些进行排序,所以当我输出stores+距离值时,它是从最短到最长的距离排序。我想把List<DataRow>List<Double>组合切换到Dictionary<Double, DataRow>,并在那里使用排序,但当然在某些情况下,两个位置的距离值相同,因此它不会是唯一的。我可以使用其他集合类型吗?或者你能推荐一种对数据进行排序的好方法吗?

这是我的代码,以防您需要视觉效果:

PRA对象基本上是我们用于处理CMS后端方面的主要对象。在本例中,我使用了一种简单的方法来查询数据,并检查请求数据中的几个变量。剩下的都是.net内置的东西。

List<DataRow> locationsInRange = new List<DataRow>();
List<Double> distances = new List<Double>();
if(!String.IsNullOrEmpty(PRA.Request.QueryString["lat"]) && !String.IsNullOrEmpty(PRA.Request.QueryString["lng"])) {
  Double earthRadius = 6371.0;
  Double maxDistance = 50.0;
  var locations = PRA.QueryDataRows("*", "", "{Location}", "showweb");
  Double lat =  Double.Parse(PRA.Request.QueryString["lat"]);
  Double lng =  Double.Parse(PRA.Request.QueryString["lng"]);
  if(!String.IsNullOrEmpty(PRA.Request.QueryString["radius"])) {
    Double temp = Double.Parse(PRA.Request.QueryString["radius"]);
    if(temp > 0) {
      maxDistance = temp;
    }
  }
  bool firstError = true;
  foreach(var l in locations) {
    Double latSecond = 0.0;
    Double lngSecond = 0.0;
    try {
      latSecond = Double.Parse(l["lat"].ToString());
      lngSecond = Double.Parse(l["lng"].ToString());
    }
    catch(Exception ex) {
      // do nothing. The lat and lng may not of been valid or not returned a result
    }

    Double dLat = Math.PI * ((lat - latSecond) / 180.0);
    Double dLon = Math.PI * ((lng - lngSecond) / 180.0);
    Double lat1 = Math.PI * (latSecond / 180.0);
    Double lat2 = Math.PI * (lat / 180.0);
    Double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Sin(dLon / 2) * Math.Sin(dLon / 2) * Math.Cos(lat1) * Math.Cos(lat2);
    Double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
    if(earthRadius * c <= (Double) maxDistance) {
      locationsInRange.Add(l);
      distances.Add(Math.Round(earthRadius * c, 1));
    }
  }
}

按相同顺序对两个单独的列表进行排序

使用由Tuple类型表示的对(数据、距离)列表。。。

var locations = new List<Tuple<DataRow, double>>();
locations.Add(Tuple.Create(row, distance));
locations.Sort((x, y) => x.Item2.CompareTo(y.Item2));

您可以在方法中间使用var关键字,让编译器生成一个匿名类。

var combinedInstance = new {Row = new DataRow(), Distance = 0.0m};

您可以在方法的中间使用lambda表示法,让编译器生成新的方法。

Func<Location, Location, decimal> getDistance = (loc1, loc2) =>
{
  return 0.0m; //TODO implement
} 

var combinedList = locationsInRange
  .Select((row, i) => new {Row = row, Distance = distances[i]})
  .OrderBy(x => x.Distance)
  .ToList();
// now replace the original lists with newly ordered lists.
locationsInRange = combinedList.Select(x => x.Row).ToList();
distances = combinedList.Select(x => x.Distance).ToList();

如果不能重构为单个数据类型,则可以对索引进行排序并使用查找表。这对于与一些遗留代码库接口来说已经足够好了。

独立示例:

using System;
using System.Collections.Generic;
class Program
{
    static void Main()
    {
        // Some disjoint datasets
        List<int> scores = new List<int>{3,5,2,8,4};
        List<string> names = new List<string>{"three","five","two","eight","four"};
        // Sequence of indices
        List<int> indices = new List<int>(System.Linq.Enumerable.Range(0, scores.Count));
        // Sort indices, based on corresponding score
        indices.Sort(delegate(int a, int b) { return scores[a] - scores[b]; });
        for(int i = 0; i < indices.Count; ++i) 
        {
            // Use lookup table for indices
            int index = indices[i];
            Console.WriteLine(string.Format("Name: {0}, score: {1}", names[index], scores[index]));
        }
    }
}

干净的方法是创建一个包含DataRowDoubleICompareable类,比较默认为Double的比较。然后可以使用SortedList作为自然表示。

由于根据应用程序的设计(我第一次否决),这种干净的方式是不可能的,我们需要一种肮脏的方式,我的建议是

  • 假设你的购物清单中有一些独特的价值(地点似乎就是其中之一)
  • 创建排序为String.Format("{0:0000000000}/{1}",Math.Round(distance*10000),unique_criterium)的字符串表示
  • 将其存储在SortedList中
  • 迭代SortedList时,将String解析回