按位置对位于相同位置的服务器列表进行排序,然后加载

时间:2016-09-13 20:42:44

标签: c# linq sorting

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

  • LOC1 S1:加载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并将其推送到输出列表的后面)。然后对每个组中的第二个加载项目重复相同的操作。如果我们应用那个逻辑,那么我们应该得到:

按距离排序的每组中首先载入最少的项目:

第一步是按距离分组的有序项目列表。 距离是每个小组的关键。

从第一组开始至少

  • Loc 2 S2:负载0.1 - 1km

下一组是Loc 3

  • Loc 3 S1:负载0.1 - 2km

下一组是Loc 1

  • Loc 1 S2:负载0.1 - 3km

从第一组最少装载

  • LOC2 S3:加载0.2 - 2km

下一组

  • LOC1 S4:加载0.2 -3km

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

最后加载第三项的是......

  • 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

第二组

  • LOC2 S3:加载0.2 - 2km

  • LOC1 S4:加载0.2 -3km

第3组

  • 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 = servers.Where(x => x.Load!= null).OrderBy(x => x.Load).ThenBy(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

由于距离应该先到,然后加载Loc2 S1 Load 0.7应该在Loc1 S3之前加载0.6 @shameel

我正在审查我的伪,因此我想出了我的实现:

   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++;
        } 
    }

与你的唯一区别是加上我认为它是ElementAtDefault(0) 应该工作相同。 此致

Shameel

2 个答案:

答案 0 :(得分:0)

为什么不尝试这样的事情?

成为System.out.printf("The number %d is cool!", variable); 原始(非分组)服务器列表:

servers

如果我理解得那么,那应该给你一个按最近距离排序的列表,然后按最小负载排序。

如果你想添加&#34; null load&#34;到列表的末尾:

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

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

编辑:那个人并不尊重团体之间的确切顺序。为了达到这个目的,你应该手动迭代&#34;通过每个小组,使用此代码和您的测试用例,我得到了预期的结果:

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

(注意:为了简单起见,我使用int类型进行了测试...只是为了在键入时保存一些字符:P)

答案 1 :(得分:0)

有趣的问题。我不知道这是否可以以优雅的方式完成,但您可以将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;
    }

}