我正在研究一种可以"解决"客户问题的机器。
我的客户的问题是这样的:
public class ClientProblem
{
public int ClientID { get; set; }
public string TaskToSolve { get; set; }
}
我每天都在准备今天要解决的所有问题的清单。列表如下所示:
List<ClientProblem> tasks = new List<ClientProblem>();
// Filling the list with task to be solved today
// list[0] = client 1 task 1
// list[1] = client 1 task 2
// ...
// list[n-1] = client 1 task n
// list[n+1] = client 2 task 1
// list[n+2] = client 2 task 2
// ...
// list[2n-1] = client 2 task 2n
// ...
我得到的问题是客户N的问题,只有在解决了所有其他客户的问题之后。这让客户N饿了。
我想混合所有的问题,而不是饿死客户。解决方案应该像这样:
// list[0] = client 1 task 1
// list[1] = client 2 task 1
// ...
// list[n] = client n task 1
// list[n+1] = client 1 task 2
// list[n+2] = client 2 task 2
// ...
// list[2n] = client n task 2
// ...
我认为我应该使用Linq GroupBy和Join方法。有可能用Linq解决这个问题吗?有没有其他有效的解决办法?
定义了以下扩展方法后
public static class SomeExtensions
{
public static IEnumerable<T> InterleaveBy<T, S>(this IEnumerable<T> input, Func<T, S> selector)
{
return input
.GroupBy(selector)
.SelectMany(g => g.Select((x, i) => new { key = i, value = x }))
.OrderBy(x => x.key)
.Select(x => x.value);
}
}
你可以用
var newList = tasks.InterleaveBy(c=>c.ClientID).ToList();
如果你有一个列表的列表,那么
var newList = tasks.SelectMany(x => x).InterleaveBy(c=>c.ClientID).ToList();
您需要的是按客户端ID对任务进行分组,然后将这些组穿插在一起。恐怕LINQ没有交错方法,但你可以创建自己的方法,例如:
public static IEnumerable<T> Interleave<T>( IEnumerable<IEnumerable<T>> sequences )
{
var enumerators = sequences.Select( s => s.GetEnumerator() ).ToArray();
while ( true )
{
foreach ( var e in enumerators )
{
if ( e.MoveNext() )
{
yield return e.Current;
}
else
{
yield break;
}
}
}
}
该方法假定所有序列的长度相同;如果不是这种情况,则需要添加一个标志,指示枚举器是否返回任何元素,并使循环条件依赖于此,而不是在一个序列结束时立即使用yield break
。