按距离对位于同一位置的服务器列表进行排序

本文关键字:列表 服务器 排序 位置 距离 于同一 | 更新日期: 2023-09-27 18:07:03

我有一个服务器列表(标题,负载,距离)初始列表已按距离分组。

  • LOC1 S1: Load NULL - 3km
  • LOC1 S2:负载0.1 - 3km
  • LOC1 S3:负载0.6 - 3km
  • LOC1 S4:负荷0.2 - 3km
  • LOC2 S1:负载0.7 - 1km
  • LOC2 S2:负载0.1 - 1km
  • LOC2 S3:负载0.2 - 1km
  • LOC3 S1:负载0.1 - 2km

我正试图写一个Linq查询,做以下事情:对于每个按位置分组的服务器列表,取加载最少的项(带null的加载项应该被忽略并推到输出列表的后面)。然后对每组中第二个加载最少的项目重复相同的操作。如果我们应用这个逻辑,那么我们应该得到:

每组按距离排序的第一个最小加载项:

第一步是有一个按距离分组的有序列表。因为距离是每组的关键。

From first group first least

  • Loc 2 S2:负载0.1 - 1km

下一组是Loc 3

  • Loc 3 S1:负载0.1 - 2km

下一组为Loc 1

  • Loc 1 S2:负载0.1 - 3km

From first group From second least loaded

  • LOC2 S3:负载0.2 - 2km
下组

  • LOC1 S4:负载0.2 -3km

注意这里没有位置3,因为它没有第二个加载0.2

最后第三个最少装载的项目是…

  • LOC2 S1:负载0.7 - 1km
下组

  • LOC 1 S3:负载0.6 3km

如果我们把这些组合在一起,我们有:

1组:

  • Loc 2 S2:负载0.1 - 1km

  • Loc 3 S1:负载0.1 - 2km

  • Loc 1 S2:负载0.1 - 3km

2组

  • LOC2 S3:负载0.2 - 2km

  • LOC1 S4:负载0.2 -3km

第三组

  • LOC2 S1:负载0.7 - 1km
下组

  • LOC 1 S3:负载0.6 3km

最后将这些加起来,按照每组的距离和负载进行排序:

  • LOC 2 S2:负载0.1 - 1km

  • LOC 3 S1:负载0.1 - 2km

  • LOC 1 S2:负载0.1 - 3km

  • LOC 2 S3:负载0.2 - 2km

  • LOC 1 S4:负载0.2 - 3km

  • LOC 2 S1:负载0.7 - 1km

  • LOC 1 S3:负载0.6 - 3km

我已经尝试了以下同步:

var orderedbyLoad = servers.OrderBy(t=>t.Load);
var groupByDistance = orderedbyLoad.GroupBy(a=>a.Distance,s=>s);

这应该给出一个按距离排序的组,以及每个组中按负载排序的服务器列表。

下一步是我有问题的地方。我想对每个组使用某种取逻辑,然后循环遍历每个组中的下一个加载项。这些需要都项目到一个平面列表,如上所示,尊重距离,然后加载顺序,但我不确定如何用linq做到这一点?

问题出在最后两项。如果我运行你的查询,我会得到var groupedByDistance =服务器。其中(x => x. load != null)。order (x => x. load)。then (x => x. distance);

:

  • LOC 2 S2 Load 0.1 Dist 1

  • LOC 3 S1 Load 0.1 Dist 2

  • LOC 1 S2 Load 0.1 Dist 3

  • LOC 2 S3 Load 0.2 Dist 1

  • LOC 1 S4 Load 0.2 Dist 3

  • LOC 1 S3 Load 0.6 Dist 3

  • LOC 2 S1 Load 0.7 Dist 1

由于距离应该是第一位的,那么load Loc2 S1 load 0.7应该在Loc1 S3 load 0.6之前@shameel

我正在检查我的pseudo,因此我想出了我的实现:

   static IEnumerable<Server> Zip(IEnumerable<IEnumerable<Server>> groups)
    {
       bool repeat = true;
       int i=0;
       while(repeat)
       {
            repeat = false;
            foreach (var grp in groups)
            {
                var element = grp.Skip(i).ElementAtOrDefault(0);
                if (element != null)
                {
                    repeat = true;
                    yield return element;
                }
            }
            i++;
        } 
    }

与你的唯一不同是当加上我认为它应该是一样的。关于

Shameel

按距离对位于同一位置的服务器列表进行排序

有趣的问题。我不知道这是否可以以一种优雅的方式完成,但是您可以将groupByDistance传递给这样的方法:

IEnumerable<Server> Zip(IEnumerable<IEnumerable<Server>> groups)
{
    bool cont;
    int i = 0;
    do
    {
        cont = false;
        foreach (var grp in groups)
        {
            var element = grp.Skip(i).FirstOrDefault();
            if (element != null)
            {
                cont = true;
                yield return element;
            }
        }
        ++i;
    } while (cont);
}

将返回一个IEnumerable,因此如果需要,它可以在进一步的LINQ查询中使用。它还具有延迟枚举的特性。但是——它绝对不优雅。

我的完整的沙盒来尝试这个,//option2是你的选择:

using System.Collections.Generic;
using System.Linq;
namespace LinqTest01
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Server> servers = new List<Server>{
                new Server(){ load = null, distance = 3 },
                new Server(){ load = 0.1, distance = 3 },
                new Server(){ load = 0.6, distance = 3 },
                new Server(){ load = 0.2, distance = 3 },
                new Server(){ load = 0.7, distance = 1 },
                new Server(){ load = 0.1, distance = 1 },
                new Server(){ load = 0.2, distance = 1 },
                new Server(){ load = 0.1, distance = 2 },
                };
            // option 1
            var sorted = from server in servers orderby server.load where server.load != null select server;
            var groups = from server in sorted
                       group server by server.distance into bydistance
                       orderby bydistance.Key
                       select bydistance
                       ;
            var final1 = Zip(groups);
            // option 2
            var orderedbyLoad = servers.OrderBy(t => t.load);
            var groupByDistance = orderedbyLoad.GroupBy(a => a.distance, s => s);
            var final2 = Zip(groups);
        }
        static IEnumerable<Server> Zip(IEnumerable<IEnumerable<Server>> groups)
        {
            bool cont;
            int i = 0;
            do
            {
                cont = false;
                foreach (var grp in groups)
                {
                    var element = grp.Skip(i).FirstOrDefault();
                    if (element != null)
                    {
                        cont = true;
                        yield return element;
                    }
                }
                ++i;
            } while (cont);
        }
    }
    class Server
    {
        public double? load;
        public int distance;
    }
}

你为什么不试试这样做呢?

作为servers的原始(非分组)服务器列表:

servers.Where(x => x.Load != null).OrderBy(x => x.Load).ThenBy(x => x.Distance)

如果我没理解错的话,这应该会给你一个列表,按照最近的距离排序,然后是最小的负载。

如果你想把"null loads"添加到列表的末尾:

servers.Where(x => x.Load != null).OrderBy(x => x.Load).ThenBy(x => x.Distance).Concat(servers.Where(x => x.Load == null))

这是你想要达到的目标吗?

编辑:那一个没有尊重组之间的确切顺序。为了实现您应该"手动"遍历每个组,使用此代码和您的测试用例,我得到了预期的结果:

        List<Server> serversList = new List<Server>();
        serversList.Add(new Server { Name = "S1", Location = "LOC1", Distance = 3, Load = null });
        serversList.Add(new Server { Name = "S2", Location = "LOC1", Distance = 3, Load = 1 });
        serversList.Add(new Server { Name = "S3", Location = "LOC1", Distance = 3, Load = 6 });
        serversList.Add(new Server { Name = "S4", Location = "LOC1", Distance = 3, Load = 2 });
        serversList.Add(new Server { Name = "S1", Location = "LOC2", Distance = 1, Load = 7 });
        serversList.Add(new Server { Name = "S2", Location = "LOC2", Distance = 1, Load = 1 });
        serversList.Add(new Server { Name = "S3", Location = "LOC2", Distance = 1, Load = 2 });
        serversList.Add(new Server { Name = "S1", Location = "LOC3", Distance = 2, Load = 1 });
        List<Server> orderedList = new List<Server>();
        var serversByDistance = serversList.Where(x => x.Load != null)
            .OrderBy(x => x.Distance)
            .GroupBy(x => x.Distance)
            .ToDictionary(x => x.Key, x => x.OrderBy(s => s.Load).ToList());
        for (int i = 0; i < serversByDistance.Values.Max(x => x.Count); i++)
        {
            foreach (var key in serversByDistance.Keys)
            {
                var element = serversByDistance[key].ElementAtOrDefault(i);
                if (element != null)
                    orderedList.Add(element);
            }
        }

如果需要,可以将null结果添加到orderedList

(注意:为了简单起见,我使用int类型进行测试…P)