Linq分组使用两个约束

时间:2014-09-07 00:16:36

标签: c# linq

必要的免责声明:我希望帮助更好地理解Linq,以便我尝试在工作中解决现实问题并使用Linq解决它。这并不意味着我假设Linq比更直接的程序解决方案更好(更快,更少内存,更具可读性等)。如果有的话,我正试图找到关于我的工作的Linq(和功能风格)的边缘。

作为更大的通信协议的一部分,我有一个List<byte[]>。这些byte[]的大小可变,每个都代表对某个系统的请求。这些byte[]必须以与List<>中相同的顺序发送到另一个系统,但是最好将几个byte[]请求打包到单个消息中以减少通信开销。消息的有效负载将是单个byte[],它是所有单个byte[]的串联。

byte[]的这种打包有两个限制。第一个是我们对可以发送的总字节数(maxSendBytes)有一个上限。第二个是每个单独的请求将生成一些最大长度的可变长度响应。这些响应的最大长度之和不能大于另一个限制(maxReceiveBytes)。我们可以使用名为LongestResponseLength的函数来确定响应的最大长度,该函数接受请求的byte[]

因此,理想的最终结果是将表示单个请求的一个List<byte[]>转换为一个List<byte[]>,其中每个byte[]是由串联组成的单个消息有效负载一个或多个byte[]个请求。

这是我的程序解决方案,可以帮助人们理解我正在尝试做的事情:

public static List<byte[]> PackList(List<byte[]> options, int maxSendBytes = 500, int maxReceiveBytes = 1500-200)
{
    var payloads = new List<byte[]>();      // each item is an outbound payload to put in a message
    var currentPayload = new List<byte>();  // payload we're accumulating on now
    var currentExpectedLength = 0;          // expected length of response payload

    foreach (var option in options)
    {
        var optionLength = LongestResponseLength(option);

        // add to current payload as long as neither limit hit

        if (currentPayload.Count + optionLength >= maxSendBytes ||
            currentExpectedLength + optionLength >= maxReceiveBytes)
        {
            payloads.Add(currentPayload.ToArray());     // add the payload to the list of payloads
            currentPayload = new List<byte>();          // start a new payload
            currentExpectedLength = 0;                  //   "
        }

        currentPayload.AddRange(option);
        currentExpectedLength += optionLength;
    }

    if (currentPayload.Count > 0)
    {
        payloads.Add(currentPayload.ToArray());
    }

    return payloads;
}

有没有更好的方法来表达上述代码?大概。但那不是问题。

1 个答案:

答案 0 :(得分:1)

我很失望,如果不依赖于GroupBy声明中的副作用,我无法解决这个问题,但我认为这符合您的要求:

var rnd = new Random(0);
//make a list of mock messages
var individualMessages = Enumerable
    .Range(0, 1000)
    .Select(_ => Enumerable
        .Range(0, rnd.Next(1,100))
        .Select(__ => (byte)rnd.Next(255))
        .ToArray())
    .ToList();

var maxMessageSize = 1000;

var total = 0;
var groupingNum = 0;
var aggregatedMessages = individualMessages
    .GroupBy(x => {
        total += x.Length;
        if(total > maxMessageSize){
            groupingNum++;
            total = x.Length;
        }
        return groupingNum;
    })
    .Select(x => x.SelectMany(v => v).ToArray())
    .ToList();