2011-08-05 295 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; 
    }