2011-08-05 7 views
10

もっと簡単な方法で次のことを行うことができるファンシーなLINQ式がありますか?私はList<List<double>>を持っていますが、Listが2次元行列の列であると仮定して、列のリストを行のリストに入れ替えたいと思います。私は、次の明白な解決策を持っている:ここでは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#コードは、無料のインタラクティブなC#プログラミングツールLinqPadにカットアンドペーストして動作します。

私は、後置演算子(つまり、拡張メソッド) "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()); 
    } 

が完全な実装であり、関数の仕組みを監視するためにコメントを外すことができる行がいくつかコメントされています。行内側(第2)の最初のインデックスと列を指す:

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

Iは時々反転形式元の答えを列と行を持っていたか、私が使用してい慣例からの、上記回答のいくつかを組み合わせていインデックス。例えば値[行] [列]

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; 
    }    
関連する問題