C# 列表<;字符串[]>;通过Linq确定最大长度
我有以下结构C# 列表<;字符串[]>;通过Linq确定最大长度,c#,linq,list,C#,Linq,List,我有以下结构 List<string[]> sList = new List<string[]>() { new[] { "x", "xxx", "xxxx" }, //1,3,4 new[] { "x", "xx", "xx" }, //1,2,2 new[] { "xxxxxx", "xx", "xx" } //6,2,2 }; 是否有一种简单的Linq方法 我的努力(工作但不使用Linq): 列表结果=新列表(); foreach
List<string[]> sList = new List<string[]>() {
new[] { "x", "xxx", "xxxx" }, //1,3,4
new[] { "x", "xx", "xx" }, //1,2,2
new[] { "xxxxxx", "xx", "xx" } //6,2,2
};
是否有一种简单的Linq方法
我的努力(工作但不使用Linq):
列表结果=新列表();
foreach(表中的字符串[]行)
{
for(int i=0;ii)
{
if(结果[i]<行[i].长度)
{
结果[i]=行[i]。长度;
}
}
其他的
{
结果。插入(i,行[i]。长度);
}
}
}
行/列的数量是动态的,但每行的列数相同。一种方法:
int maxColumns = sList.Max(arr => arr.Length);
List<int> Result = Enumerable.Range(0, maxColumns)
.Select(i => sList.Max(arr => (arr.ElementAtOrDefault(i) ?? "").Length))
.ToList();
这是我能想到的最好的办法:
List<int> Result = sList.First().Select((x, i) => sList.Max(y => y[i].Length)).ToList();
List Result=sList.First().Select((x,i)=>sList.Max(y=>y[i].Length)).ToList();
一条线的加分
说明:
既然你说它们都有相同数量的元素,那么就从第一行开始循环。使用索引从每个其他行中获取该元素,获取长度,然后获取其最大值。假设所有“行”的项数(“列”)相同:
这就是我所拥有的:-
int arrayLength = sList.First().Length;
List<int> result = sList.SelectMany(x => x)
.Select((v, i) => new { Value = v, Index = i % arrayLength })
.GroupBy(x => x.Index)
.Select(x => x.Max(z => z.Value.Length))
.ToList();
int arrayLength=sList.First().Length;
列表结果=sList.SelectMany(x=>x)
.Select((v,i)=>new{Value=v,Index=i%arrayLength})
.GroupBy(x=>x.Index)
.选择(x=>x.Max(z=>z.Value.Length))
.ToList();
说明:-展平列表,投影其值和索引(索引根据列长度计算),按索引分组并选择最大长度的值。以下是使用以下方法的方法:
这样做的前提是每个集合都有相同数量的项目
工作原理:
- 首先,我们只取字符串的长度,因此从概念上讲,我们使用的是
,因此我们的集合如下所示:IEnumerable
{{1, 3, 4}, {1, 2, 2}, {6, 2, 2}}
(这是不准确的-因为我们从未实际拥有此集合-根据需要计算每行的长度。)List<Int> seed = ??? Func<IEnumerable<Int>, IEnumerable<Int>, IEnumerable<Int>> accumulator = ??? llist.Aggregate(seed, accumulator);
- 接下来,我们使用
组合向量:Aggregate
- 我们有
和{1,3,4}
{1,2,2}
- 这两个集合:
{{1,2},{3,2},{4,2}
- 对每一对应用
,并获得Math.Max
{2,3,4}
- 我们有
- 对
和{2,3,4}
重复上述操作,然后接收{6,2,2}
{6,3,4}
- 适用于单行
- 仅在集合上迭代一次
- 不对具体实现(数组列表)进行任何假设-可以使用任何
,而不依赖IEnumerable
或[i]
ElementAtOrDefault
- 您已请求Linq解决方案
- 有点复杂
- 如果行的长度不同,
将不符合预期(返回最短的长度,而不是最长的长度)Zip
聚合
扩展方法
tl;rd对于最简单的情况:
slist.Select(row => row.Select(str => str.Length))
.Aggregate(l, r) => l.Zip(r, Math.Max);
首先,让我们以我们想要的形式获取数据:长度列表
var llist = slist.Select(row => row.Select(str => str.Length));
现在我们有一个长度列表。更容易相处
Aggregate的工作原理是保持一些运行总计,并运行一个参数为Aggregate和下一行的函数,从而生成一个新的聚合。您可以选择提供种子值。看起来是这样的:
{{1, 3, 4},
{1, 2, 2},
{6, 2, 2}}
List<Int> seed = ???
Func<IEnumerable<Int>, IEnumerable<Int>, IEnumerable<Int>> accumulator = ???
llist.Aggregate(seed, accumulator);
如果列数不变,我们可以使用内置Zip
llist.Aggregate((l, r) => l.Zip(r, Math.Max));
(1) 在这个问题的范围之外,还有一个更普遍的过载
IEnumerable<TResult, TLeft, TRight> ZipAll(IEnumerable<TLeft> left, IENumerable<TRight> right, Func<TResult, TLeft, TRight> selector, Func<TResult, TLeft> leftSelector, Func<Tresult, TRight> rightselector) {
var le = left.GetEnumerator;
var re = right.GetEnumerator;
while(le.MoveNext()){
if (re.MoveNext()) {
yield return selector(le.Current, re.Current);
} else {
yield return leftSelector(le.Current);
}
}
while(re.MoveNext()) yield return rightSelector(re.Current);
}
IEnumerable ZipAll(IEnumerable left,IEnumerable right,Func选择器,Func leftSelector,Func rightselector){
var le=left.GetEnumerator;
var re=right.GetEnumerator;
while(le.MoveNext()){
if(re.MoveNext()){
收益率返回选择器(le电流、re电流);
}否则{
收益返回左选择器(当前值);
}
}
而(re.MoveNext())则返回rightSelector(re.Current);
}
有了这个,我们可以用左、右投影作为身份函数来写我们早期的ZipAll
IEnumerable<T> ZipAll(this IEnumerable<T> left, IENumerable<T> right, Func<T, T, T> selector) {
return ZipAll(left, right, selector, id => id, id => id);
}
IEnumerable ZipAll(此IEnumerable左、IEnumerable右、函数选择器){
返回ZipAll(左、右、选择器,id=>id,id=>id);
}
令人惊讶的是,没有任何好的(有效的)答案
1) 不要使用LINQ。尝试一种简单易读的程序解决方案2) 如果出于某种原因您想使用LINQ,至少要使其高效 下面是我的示例代码的结果
使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.Linq;
命名空间控制台应用程序9
{
内部课程计划
{
私有静态void Main(字符串[]args)
{
Random rand=新的Random();
int maxColumnLength=10;
整数行数=100000;
int colNumber=300;
列表输入=新列表();
for(int i=0;iList<Int> seed = new List<Int>();
Func<List<Int>, List<Int>, List<Int>> accumulator = ???
llist.Aggregate(seed, accumulator);
private IEnumerable<Int> accumulate(aggregate, row){
aggregate.Zip(row, (i1, i2) => Math.max(i1, i2));
}
IEnumerable<T> ZipAll(IEnumerable<T> left, IENumerable<T> right, Func<T, T, T> selector) {
var le = left.GetEnumerator;
var re = right.GetEnumerator;
while(le.MoveNext()){
if (re.MoveNext()) {
yield return selector(le.Current, re.Current);
} else {
yield return le.Current;
}
}
while(re.MoveNext()) yield return re.Current;
}
List<Int> seed = new List<Int>();
Func<List<Int>, List<Int>, List<Int>> accumulator = (l, r) => ZipAll(l, r, Math.Max);
llist.Aggregate(seed, accumulator);
llist.Aggregate(new List<Int>(), (l, r) => ZipAll(l, r, Math.Max));
llist.Aggregate((l, r) => ZipAll(l, r, Math.Max));
llist.Aggregate((l, r) => l.Zip(r, Math.Max));
IEnumerable<TResult, TLeft, TRight> ZipAll(IEnumerable<TLeft> left, IENumerable<TRight> right, Func<TResult, TLeft, TRight> selector, Func<TResult, TLeft> leftSelector, Func<Tresult, TRight> rightselector) {
var le = left.GetEnumerator;
var re = right.GetEnumerator;
while(le.MoveNext()){
if (re.MoveNext()) {
yield return selector(le.Current, re.Current);
} else {
yield return leftSelector(le.Current);
}
}
while(re.MoveNext()) yield return rightSelector(re.Current);
}
IEnumerable<T> ZipAll(this IEnumerable<T> left, IENumerable<T> right, Func<T, T, T> selector) {
return ZipAll(left, right, selector, id => id, id => id);
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace ConsoleApplication9
{
internal class Program
{
private static void Main(string[] args)
{
Random rand = new Random();
int maxColumnLength = 10;
int rowNumber = 100000;
int colNumber = 300;
List<string[]> input = new List<string[]>();
for (int i = 0; i < rowNumber; i++)
{
string[] row = new string[colNumber];
for (int j = 0; j < colNumber; j++)
row[j] = new string('x', rand.Next(maxColumnLength));
input.Add(row);
}
var result = Time(SimpleIteration, input, "Simple Iteration");
var result2 = Time(SimpleIterationWithLinq, input, "Simple Iteration LINQ");
var result3 = Time(AcceptedAnswer, input, "Accepted Answer");
var result4 = Time(TomDoesCode, input, "TomDoesCode");
//var result5 = Time(Kobi, input, "Kobi"); //StackOverflow
var result6 = Time(Konamiman, input, "Konamiman");
var result7 = Time(RahulSingh, input, "RahulSingh");
System.Console.ReadLine();
}
private static List<int> SimpleIteration(List<string[]> input)
{
int[] maxPerColumn = new int[input.First().Length];
for (int i = 0; i < maxPerColumn.Length; i++)
maxPerColumn[i] = -1;
foreach (var row in input)
{
for (int i = 0; i < row.Length; i++)
if (maxPerColumn[i] < row[i].Length)
maxPerColumn[i] = row[i].Length;
}
return maxPerColumn.ToList();
}
private static List<int> SimpleIterationWithLinq(List<string[]> input)
{
return input.Aggregate(new int[input.First().Length], (maxPerColumn, row) =>
{
for (int i = 0; i < row.Length; i++)
if (maxPerColumn[i] < row[i].Length)
maxPerColumn[i] = row[i].Length;
return maxPerColumn;
}).ToList();
}
private static List<int> AcceptedAnswer(List<string[]> input)
{
return Enumerable.Range(0, input.First().Length)
.Select(i => input.Max(arr => arr[i].Length))
.ToList();
}
private static List<int> TomDoesCode(List<string[]> input)
{
return input.First().Select((x, i) => input.Max(y => y[i].Length)).ToList();
}
private static List<int> Kobi(List<string[]> input)
{
return input.Select(strings => strings.Select(s => s.Length))
.Aggregate((prev, current) => prev.Zip(current, Math.Max)).ToList();
}
private static List<int> Konamiman(List<string[]> input)
{
var rowLength = input[0].Length;
return Enumerable.Range(0, rowLength)
.Select(index => input.Select(row => row[index].Length).Max())
.ToList();
}
private static List<int> RahulSingh(List<string[]> input)
{
int arrayLength = input.First().Length;
return input.SelectMany(x => x)
.Select((v, i) => new { Value = v, Index = i % arrayLength })
.GroupBy(x => x.Index)
.Select(x => x.Max(z => z.Value.Length))
.ToList();
}
private static List<int> Time(Func<List<string[]>, List<int>> act, List<string[]> input, string methodName)
{
Stopwatch s = Stopwatch.StartNew();
var result = act(input);
s.Stop();
Console.WriteLine(methodName.PadRight(25) + ":" + s.ElapsedMilliseconds + " ms");
return result;
}
}
}