如何根据下一个属性对集合进行排序?

时间:2019-09-19 19:51:35

标签: c# sorting model-view-controller

我正在尝试计算在C#中使用GeoCoordinates的所有站点之间的最佳路线。

我有一些方法可以获取列表中的下一个最近位置,但是我需要能够通过以下方式对原始列表进行排序:

start -> next stop -> next stop -> next stop -> finish

这是我目前正在使用的:

模型

public class DispatchStopModel
{
    public long OrderDispatchID { get; set; }
    public long? NextDispatchID { get; set; }
    public PhysicalAddress PhysicalAddress { get; set; }
    public double Distance { get; set; }
}

方法

    private static double DistanceTo(double lat1, double lon1, double lat2, double lon2, char unit = 'M')
    {
        double rlat1 = Math.PI * lat1 / 180;
        double rlat2 = Math.PI * lat2 / 180;
        double theta = lon1 - lon2;
        double rtheta = Math.PI * theta / 180;
        double dist =
            Math.Sin(rlat1) * Math.Sin(rlat2) + Math.Cos(rlat1) *
            Math.Cos(rlat2) * Math.Cos(rtheta);
        dist = Math.Acos(dist);
        dist = dist * 180 / Math.PI;
        dist = dist * 60 * 1.1515;

        switch (unit)
        {
            case 'K': //Kilometers -> default
                return dist * 1.609344;
            case 'N': //Nautical Miles 
                return dist * 0.8684;
            case 'M': //Miles
                return dist;
        }

        return dist;
    }

    private void FindClosestStop(DispatchStopModel start, ICollection<DispatchStopModel> stops)
    {
        if(start.PhysicalAddress?.HasGeocode ?? false)
        {
            foreach(var stop in stops.Where(s => s.OrderDispatchID != start.OrderDispatchID))
            {
                if(stop.PhysicalAddress?.HasGeocode ?? false)
                {
                    double distanceTo = DistanceTo((double)start.PhysicalAddress.Latitude, (double)start.PhysicalAddress.Longitude, (double)stop.PhysicalAddress.Latitude, (double)stop.PhysicalAddress.Longitude);

                    if (distanceTo < start.Distance)
                    {
                        start.Distance = distanceTo;
                        start.NextDispatchID = stop.OrderDispatchID;
                    }
                }
            }
        }
    }

现在这是我的控制器动作:

public IActionResult GetDeliveries()
{
    var deliveries = deliveryService.GetYourDeliveries();

    var Stops = Deliveries.Select(d => new DispatchStopModel
    {
        OrderDispatchID = d.OrderDispatchID,
        NextDispatchID = null,
        PhysicalAddress = d.Order.OrderAddress?.PhysicalAddress,
        Distance = double.MaxValue
    })
    .ToArray();


    foreach(var stop in Stops)
    {
        FindClosestStop(stop, Stops);
    }

    //How can I sort on deliveries using the "NextDispatchID" from the collection I just generated?
}

我当时想的是,我可以设置一个新列表,并在OrderDispatchID等于站点的OrderDispatchID的位置手动插入每个交货。我觉得这样效率很低。

对此有正确的想法吗?

预先感谢

1 个答案:

答案 0 :(得分:2)

从高层次看您的问题,在我看来,您实际上正在处理一个链表,其中每个项目都指向下一个项目。

如果是这种情况,则为每个项目设置“下一个”项目的一种方法是:复制所有停靠点,遍历原始停靠点列表,在每次迭代中在列表副本中分配最接近的停靠点,然后从列表副本中删除该项目,以免再次分配该项目:

List<DispatchStopModel> deliveries = deliveryService.GetYourDeliveries();
List<DispatchStopModel> temp = deliveries.ToList();

// Assuming you have a start location
DispatchStopModel start = GetStartingPoint();

// Assign the closest stop to it
FindClosestStop(start, deliveries);

// Now assign the closest delivery for each of the rest
// of the items, removing the closest item on each
// iteration, so it doesn't get assigned more than once.
foreach (var delivery in deliveries)
{
    var closest = FindClosestStop(delivery, temp);
    temp.Remove(closest);
}

此后,应为所有项目分配一个NextDispatchID,该{}指向下一个最近的交货点。如果您仍然想对列表进行“排序”,此站点上有许多关于如何对链表进行排序的答案(例如,like this)。

请注意,我对FindClosestStop进行了一些更改,以使其返回找到的最接近的停靠点:

private DispatchStopModel FindClosestStop(DispatchStopModel start, 
    ICollection<DispatchStopModel> stops)
{
    DispatchStopModel closest = null;

    // other code ommitted for brevity

    if (distanceTo < start.Distance)
    {
        start.Distance = distanceTo;
        start.NextDispatchID = stop.OrderDispatchID;
        closest = stop;
    }

    // other code ommitted for brevity

    return closest;
}

还要注意,Equals类上的GetHashCodeDispatchStopModel应该被重写,以使Remove方法起作用。如果您不想这样做,则需要搜索具有匹配的OrderDispatchID的商品并将其删除。