【问题标题】:Sorting two separate Lists in the same order?以相同的顺序对两个单独的列表进行排序?
【发布时间】:2012-01-20 15:12:14
【问题描述】:

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

这是我的问题

在这个页面上,我进行了数据库查询,加载了所有 700 个商店位置。然后,我根据查询字符串中的 lat 和 lng 值与数据库中的值进行距离计算,以找到 50 公里内的商店。在那个距离内的每一个,我目前都添加到List<DataRow>。我还获取距离结果,将其四舍五入到小数点后一位,然后将其存储到 Double 类型的简单列表中。我想要做的,基本上是对这些进行排序,所以当我输出商店 + 距离值时,它是从最短到最长距离排序的。我正在考虑将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));
    }
  }

}

【问题讨论】:

  • 大多数数据库都内置了这个功能...Spatial_Index.. 你不应该手动这样做
  • 这实际上是一个非常有趣的观点。现在,lat & lng 值当前存储在 varchar 中。我们为每个客户站点设置的 CMS 表不支持这种数据类型,但我肯定会与他们讨论它,因为向前推进这将非常有用。
  • 投票结束:“我无法创建自己的类或方法”这太本地化了。

标签: c# .net


【解决方案1】:

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

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

【讨论】:

  • 非常感谢,我不得不对最后一行做一个小修正,但这成功了:locations.Sort((x, y) =&gt; x.Item2.CompareTo(y.Item2));
【解决方案2】:

可以在方法中间使用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();

【讨论】:

    【解决方案3】:

    如果不能重构为单一数据类型,您可以对索引进行排序并使用查找表。这足以与一些遗留代码库交互。

    独立示例:

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

    【讨论】:

      【解决方案4】:

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

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

      • 假设您的商店清单中有一些独特的价值(位置似乎是其中之一)
      • 创建一个字符串表示String.Format("{0:0000000000}/{1}",Math.Round(distance*10000),unique_criterium)
      • 将其存储在 SortedList 中
      • 在迭代 SortedList 时,将字符串解析回来

      【讨论】:

      • 嗨,Eugene,感谢您的建议,但根据我的帖子顶部,我无法定义任何类或方法 :(。我用于模板的代码主体实际上被拉入了一个方法块,然后被编译。也许这不是该平台最强大的设计,但我想这是当时开发时间框架内最好的解决方案。
      猜你喜欢
      • 2019-08-18
      • 2023-01-19
      • 2012-10-20
      • 1970-01-01
      • 2011-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多