是否有 LINQ 运算符可以执行此操作?
Is there a LINQ operator to do this?
我想知道是否有 LINQ 运算符可以执行此操作:
var one = new[] { "A", "B", "C" };
var two = new[] { "A", "B", "C", "D" };
var combined = new [] { one, two };
var result = Operator(combined);
Console.WriteLine(result.Should().BeEquivalentTo(new [] { "A", "A", "B", "B", "C", "C", null, "D" }));
如果很短,它应该就像每个序列都是矩阵中的一行一样。之后,它应该:
- 转置矩阵(旋转)
- 它应该推送矩阵中的每个“单元格”,返回相应的项目,或者如果单元格为空则默认。
我的意思是,图形化:
A, B, C
A, B, C, D
<Transpose>
A, A
B, B
C, C
D
result => A, A, B, B, C, C, D, null
通知
Operator
应该在 IEnumerable<IEnumerable<T>>
上工作
如您所见,我感兴趣的 Operator
使用 combined
,因此它接受应该 IEnumerable<IEnumerable<T>>
(如 SelectMany
)。
要跟上不断变化的规格有点困难。本来是一对字符串数组。我在回答中将其更改为一对 T
数组。
然后你在评论中写道“哦,不,我的意思是 N 个序列”。最后,在阅读之后,我注意到您更新了您的问题以询问表示为 IEnumerable<T>
.
的 N 个集合
与此同时,我指出我的原始答案适用于 N 个数组,变化很小。所以,这里是:
对于 N 个数组
我使用 params
关键字删除了对 combined 变量的需要。 params
关键字将 将方法的部分或全部参数组合 到一个数组中。
这里有一个可以接受 N 个数组的方法:
public static IEnumerable<T> KnitArrays<T>(params T[][] arrays)
{
var maxLen = (from array in arrays select array.Length).Max();
for (var i = 0; i < maxLen; i++)
{
foreach( var array in arrays)
{
yield return array.Length > i ? array[i] : default(T);
}
}
}
和原回答的逻辑差不多。测试代码看起来也一样:
var one = new[] { "A1", "B1", "C1" };
var two = new[] { "A2", "B2", "C2", "D2" };
var three = new[] { "A3", "B3" };
var knittedArray = KnitArrays(one, two, three);
List<string> result = knittedArray.ToList();
WriteCollectionContents(result);
其中WriteCollectionContents
吐出合集内容。在这种情况下:
"A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", null, null, "D2", null,
对于 N 个列表 and/or 数组
事实证明,相同的基本代码可以与 IList<T>
一起使用,即,对于 List<T>
和 T[]
:
public static IEnumerable<T> KnitILists<T>(params IList<T>[] ilists)
{
var maxLen = (from ilist in ilists select ilist.Count).Max();
for (var i = 0; i < maxLen; i++)
{
foreach (var ilist in ilists)
{
yield return ilist.Count > i ? ilist[i] : default(T);
}
}
}
此测试代码看起来也非常相似 - 但请注意数组和列表的混合:
var one = new[] { "A1", "B1", "C1" };
var two = new[] { "A2", "B2", "C2", "D2" };
var list3 = new List<string> { "A3", "B3" };
var knittedLists = KnitILists(one, two, list3);
result = knittedLists.ToList();
WriteCollectionContents(result);
结果完全相同:
"A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", null, null, "D2", null,
它与 IList<T>
一起工作的原因是该接口有一个 Count
属性 和一个 Item
索引器。如果您转到 ICollection<T>
,Count
属性 会保留,但您会丢失 Item
索引器。
一旦你到达 IEnumerable<T>
,Count
属性 和 Item
索引器都消失了。您可以对 IEnumerable
做的唯一事情就是遍历它。因此,逻辑需要非常不同。
我可能会想出一个解决方案。但是,它可能看起来与@gertarnold 的回答非常相似。
我正在寻找你即将发表的评论的前言,内容是关于你真正打算如何将其与 multi-dimensional 数组一起使用。
原回答如下
这样的事情怎么样:
public static IEnumerable<T> KnitArrays<T>(T[] first, T[] second)
{
var maxLen = Math.Max(first.Length, second.Length);
for (var i = 0; i < maxLen; i++)
{
yield return first.Length > i ? first[i] : default(T);
yield return second.Length > i ? second[i] : default(T);
}
}
用以下方法测试:
var one = new[] { "A", "B", "C" };
var two = new[] { "A", "B", "C", "D" };
var knittedArray = KnitArrays(one, two);
List<string> result = knittedArray.ToList();
生成一个看起来像您所要求的列表。请注意,我只是 return 一个 non-materialized IEnumerable,因为你问的是 LINQ。
试试这个
var result = Enumerable.Range(0, Math.Max(one.Count(), two.Count()))
.SelectMany(n => new[] { one.ElementAtOrDefault(n), two.ElementAtOrDefault(n) });
结果
["A","A","B","B","C","C",null,"D"]
为了使结果独立于数组的数量,函数应该遍历所有数组并继续返回,直到所有枚举都用完:
public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> source)
{
var enumerators = source.Select(e => e.GetEnumerator()).ToArray();
try
{
var next = false;
do
{
var results = enumerators.Select(enr =>
{
if (enr.MoveNext())
{
return enr.Current;
}
return default;
}).ToList();
next = results.Any(e => !Equals(default, e));
if (next)
{
yield return results;
}
}
while (next);
}
finally
{
Array.ForEach(enumerators, e => e.Dispose());
}
}
现在您可以使用任意数量的数组:
var one = new[] { "A", "B", "C" };
var two = new[] { "A", "B", "C", "D" };
var three = new[] { "U", "V","W", "X", "Y", "Z" };
var combined = new[] { one, three, two };
var result = combined.Transpose().SelectMany(e => e).ToList();
这将导致
"A","U","A","B","V","B","C","W","C",null,"X","D",null,"Y",null,null,"Z",null
过滤空值很简单。
(基本思想由 提供,但仅适用于等长数组)。
我想知道是否有 LINQ 运算符可以执行此操作:
var one = new[] { "A", "B", "C" };
var two = new[] { "A", "B", "C", "D" };
var combined = new [] { one, two };
var result = Operator(combined);
Console.WriteLine(result.Should().BeEquivalentTo(new [] { "A", "A", "B", "B", "C", "C", null, "D" }));
如果很短,它应该就像每个序列都是矩阵中的一行一样。之后,它应该:
- 转置矩阵(旋转)
- 它应该推送矩阵中的每个“单元格”,返回相应的项目,或者如果单元格为空则默认。
我的意思是,图形化:
A, B, C
A, B, C, D
<Transpose>
A, A
B, B
C, C
D
result => A, A, B, B, C, C, D, null
通知
Operator
应该在 IEnumerable<IEnumerable<T>>
如您所见,我感兴趣的 Operator
使用 combined
,因此它接受应该 IEnumerable<IEnumerable<T>>
(如 SelectMany
)。
要跟上不断变化的规格有点困难。本来是一对字符串数组。我在回答中将其更改为一对 T
数组。
然后你在评论中写道“哦,不,我的意思是 N 个序列”。最后,在阅读之后,我注意到您更新了您的问题以询问表示为 IEnumerable<T>
.
与此同时,我指出我的原始答案适用于 N 个数组,变化很小。所以,这里是:
对于 N 个数组
我使用 params
关键字删除了对 combined 变量的需要。 params
关键字将 将方法的部分或全部参数组合 到一个数组中。
这里有一个可以接受 N 个数组的方法:
public static IEnumerable<T> KnitArrays<T>(params T[][] arrays)
{
var maxLen = (from array in arrays select array.Length).Max();
for (var i = 0; i < maxLen; i++)
{
foreach( var array in arrays)
{
yield return array.Length > i ? array[i] : default(T);
}
}
}
和原回答的逻辑差不多。测试代码看起来也一样:
var one = new[] { "A1", "B1", "C1" };
var two = new[] { "A2", "B2", "C2", "D2" };
var three = new[] { "A3", "B3" };
var knittedArray = KnitArrays(one, two, three);
List<string> result = knittedArray.ToList();
WriteCollectionContents(result);
其中WriteCollectionContents
吐出合集内容。在这种情况下:
"A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", null, null, "D2", null,
对于 N 个列表 and/or 数组
事实证明,相同的基本代码可以与 IList<T>
一起使用,即,对于 List<T>
和 T[]
:
public static IEnumerable<T> KnitILists<T>(params IList<T>[] ilists)
{
var maxLen = (from ilist in ilists select ilist.Count).Max();
for (var i = 0; i < maxLen; i++)
{
foreach (var ilist in ilists)
{
yield return ilist.Count > i ? ilist[i] : default(T);
}
}
}
此测试代码看起来也非常相似 - 但请注意数组和列表的混合:
var one = new[] { "A1", "B1", "C1" };
var two = new[] { "A2", "B2", "C2", "D2" };
var list3 = new List<string> { "A3", "B3" };
var knittedLists = KnitILists(one, two, list3);
result = knittedLists.ToList();
WriteCollectionContents(result);
结果完全相同:
"A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", null, null, "D2", null,
它与 IList<T>
一起工作的原因是该接口有一个 Count
属性 和一个 Item
索引器。如果您转到 ICollection<T>
,Count
属性 会保留,但您会丢失 Item
索引器。
一旦你到达 IEnumerable<T>
,Count
属性 和 Item
索引器都消失了。您可以对 IEnumerable
做的唯一事情就是遍历它。因此,逻辑需要非常不同。
我可能会想出一个解决方案。但是,它可能看起来与@gertarnold 的回答非常相似。
我正在寻找你即将发表的评论的前言,内容是关于你真正打算如何将其与 multi-dimensional 数组一起使用。
原回答如下
这样的事情怎么样:
public static IEnumerable<T> KnitArrays<T>(T[] first, T[] second)
{
var maxLen = Math.Max(first.Length, second.Length);
for (var i = 0; i < maxLen; i++)
{
yield return first.Length > i ? first[i] : default(T);
yield return second.Length > i ? second[i] : default(T);
}
}
用以下方法测试:
var one = new[] { "A", "B", "C" };
var two = new[] { "A", "B", "C", "D" };
var knittedArray = KnitArrays(one, two);
List<string> result = knittedArray.ToList();
生成一个看起来像您所要求的列表。请注意,我只是 return 一个 non-materialized IEnumerable,因为你问的是 LINQ。
试试这个
var result = Enumerable.Range(0, Math.Max(one.Count(), two.Count()))
.SelectMany(n => new[] { one.ElementAtOrDefault(n), two.ElementAtOrDefault(n) });
结果
["A","A","B","B","C","C",null,"D"]
为了使结果独立于数组的数量,函数应该遍历所有数组并继续返回,直到所有枚举都用完:
public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> source)
{
var enumerators = source.Select(e => e.GetEnumerator()).ToArray();
try
{
var next = false;
do
{
var results = enumerators.Select(enr =>
{
if (enr.MoveNext())
{
return enr.Current;
}
return default;
}).ToList();
next = results.Any(e => !Equals(default, e));
if (next)
{
yield return results;
}
}
while (next);
}
finally
{
Array.ForEach(enumerators, e => e.Dispose());
}
}
现在您可以使用任意数量的数组:
var one = new[] { "A", "B", "C" };
var two = new[] { "A", "B", "C", "D" };
var three = new[] { "U", "V","W", "X", "Y", "Z" };
var combined = new[] { one, three, two };
var result = combined.Transpose().SelectMany(e => e).ToList();
这将导致
"A","U","A","B","V","B","C","W","C",null,"X","D",null,"Y",null,null,"Z",null
过滤空值很简单。
(基本思想由