2011-08-05 243 views
10

是否有一种奇特的LINQ表达式可以让我以更简单的方式执行以下操作。我有一个List<List<double>>,假设List是2d矩阵中的列,我想将列列表交换成行列表。我有以下明显的解决方案:LINQ将列转换为行

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); 
} 

回答

5

你可以很容易地LINQify内环:

vector.AddRange(values.Select(value => value[i]));

无论是提高可读性完全留给你!

+0

@DBM:什么是定义AddRange的? –

+0

@ Reb.Cabin:http://msdn.microsoft.com/en-us/library/z883w3dc.aspx –

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(); 
+0

+1 - 这很好你的笔记关于固定列数 – Seth

+0

嗯可读性可能不太友好。我的版本和Linq表达式的性能是否相同? – Seth

+0

@Seth:我认为它会比你的版本执行*糟糕,因为它必须变平然后重新组,而for循环直接使用列表项的索引来创建行列表 – BrokenGlass

2
var inverted = Enumerable.Range(0, columnCount) 
       .Select(index => columnList.Select(list => list[index])); 

总之,我们列举了一个范围的列索引,并用它来收集每个列表的第n个元素。

请注意,您需要检查每个列表的列数是否相同。

0

这是一个适用于矩形(非破碎)矩阵的工具。这里的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") 
      ; 
    } 
} 
1

我结合一些问题的答案以上,这有时不得不列和行反转形式的原始答案或从我习惯的惯例:行指的是第一索引和列到内(第二)指数。例如值[行] [列]

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; 
    }