是否有一个花哨的LINQ表达式可以让我以更简单的方式执行以下操作。我有List<List<double>>
,假设List是2d矩阵中的列,我想将列列表交换为行列表。我有以下明显的解决方案:
int columns = 5;
var values; // assume initialised as List<List<double>>()
var listOfRows = new List<List<double>>();
for (int i = 0; i < columns ; i++)
{
List<double> newRow = new List<double>();
foreach (List<double> value in values)
{
newRow.Add(value[i]);
}
listOfRows.Add(newRow);
}
答案 0 :(得分:5)
你可以很容易地对内循环进行LINQify:
vector.AddRange(values.Select(value => value[i]));
这是否会提高可读性完全取决于你!
答案 1 :(得分:3)
这是一个Linq表达式可以做你想要的 - 看着它我个人坚持使用嵌套的foreach循环 - 更容易阅读:
var columnList= new List<List<double>>();
columnList.Add(new List<double>() { 1, 2, 3 });
columnList.Add(new List<double>() { 4, 5, 6 });
columnList.Add(new List<double>() { 7, 8, 9 });
columnList.Add(new List<double>() { 10, 11, 12 });
int columnCount = columnList[0].Count;
var rowList = columnList.SelectMany(x => x)
.Select((x, i) => new { V = x, Index = i })
.GroupBy(x => (x.Index + 1) % columnCount)
.Select(g => g.Select( x=> x.V).ToList())
.ToList();
此示例也仅适用于具有固定列数的矩阵。基本上它将矩阵展平为一个列表,然后通过按列列表中元素的索引进行分组来创建行列表。
修改强>
一种不同的方法,更接近嵌套循环,除了开销之外可能还有类似的性能。
int columnCount = columnList[0].Count;
int rowCount = columnList.Count;
var rowList = Enumerable.Range(0, columnCount)
.Select( x => Enumerable.Range(0, rowCount)
.Select(y => columnList[y][x])
.ToList())
.ToList();
答案 2 :(得分:2)
var inverted = Enumerable.Range(0, columnCount)
.Select(index => columnList.Select(list => list[index]));
简而言之,我们从一个范围枚举列索引并使用它来收集每个列表的第n个元素。
请注意,您需要检查每个列表是否具有相同的列数。
答案 3 :(得分:1)
这是一个适用于矩形(非参差不齐)矩阵的算法。这里的C#代码可以剪切并粘贴到LinqPad中,这是一个免费的交互式C#编程工具。
我定义了一个后缀运算符(即扩展方法)“Transpose”。使用运算符如下:
var rand = new Random();
var xss = new [] {
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
};
xss.Dump("Original");
xss.Transpose().Dump("Transpose");
导致类似这样的事情:
Original
0.843094345109116
0.981432441613373
0.649207864724662
0.00594645645746331
0.378864820291691
0.336915332515219
Transpose
0.843094345109116
0.649207864724662
0.378864820291691
0.981432441613373
0.00594645645746331
0.336915332515219
此运算符的实现要点如下
public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> xss)
{
var heads = xss.Heads();
var tails = xss.Tails();
var empt = new List<IEnumerable<T>>();
if (heads.IsEmpty())
return empt;
empt.Add(heads);
return empt.Concat(tails.Transpose());
}
这是完整的实现,有些行注释掉您可以取消注释以监视函数的工作方式。
void Main()
{
var rand = new Random();
var xss = new [] {
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
};
xss.Dump("Original");
xss.Transpose().Dump("Transpose");
}
public static class Extensions
{
public static IEnumerable<T> Heads<T>(this IEnumerable<IEnumerable<T>> xss)
{
Debug.Assert(xss != null);
if (xss.Any(xs => xs.IsEmpty()))
return new List<T>();
return xss.Select(xs => xs.First());
}
public static bool IsEmpty<T>(this IEnumerable<T> xs)
{
return xs.Count() == 0;
}
public static IEnumerable<IEnumerable<T>> Tails<T>(this IEnumerable<IEnumerable<T>> xss)
{
return xss.Select(xs => xs.Skip(1));
}
public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> xss)
{
// xss.Dump("xss in Transpose");
var heads = xss.Heads()
// .Dump("heads in Transpose")
;
var tails = xss.Tails()
// .Dump("tails in Transpose")
;
var empt = new List<IEnumerable<T>>();
if (heads.IsEmpty())
return empt;
empt.Add(heads);
return empt.Concat(tails.Transpose())
// .Dump("empt")
;
}
}
答案 4 :(得分:1)
我正在结合上面的一些答案,它们有时会从原始答案或我习惯的惯例中反转出列和行:row指的是内部(第二个)索引的第一个索引和列。例如值[行] [列]
public static List<List<T>> Transpose<T>(this List<List<T>> values)
{
if (values.Count == 0 || values[0].Count == 0)
{
return new List<List<T>>();
}
int ColumnCount = values[0].Count;
var listByColumns = new List<List<T>>();
foreach (int columnIndex in Enumerable.Range(0, ColumnCount))
{
List<T> valuesByColumn = values.Select(value => value[columnIndex]).ToList();
listByColumns.Add(valuesByColumn);
}
return listByColumns;
}
实际上,行和列这个词只是我们考虑行和列中数据的惯例,有时会比解决它们更加困惑。
我们实际上只是交换外部索引的内部索引。 (或翻转指数)。所以也可以定义以下扩展方法。 。我再次从上面的解决方案中借鉴,只是将它放入我认为可读且相当紧凑的东西中。
检查内部列表是否相同。
public static List<List<T>> InsideOutFlip<T>(this List<List<T>> values)
{
if (values.Count == 0 || values[0].Count == 0)
{
return new List<List<T>>();
}
int innerCount = values[0].Count;
var flippedList = new List<List<T>>();
foreach (int innerIndex in Enumerable.Range(0, innerCount))
{
List<T> valuesByOneInner = values.Select(value => value[innerIndex]).ToList();
flippedList.Add(valuesByOneInner);
}
return flippedList;
}