目的
行列で言うところの転置をやります。
@IganinTea さんのエントリー
LINQで入れ子になっているListの並びの縦横を入れ替える
中の@yuba さんのコメントを参考に書けたので投稿します。
やること
これを
0 | 1 | 2 | |
---|---|---|---|
0 | a | b | c |
1 | d | e | f |
2 | g | h | i |
↓みたいにする
0 | 1 | 2 | |
---|---|---|---|
0 | a | d | g |
1 | b | e | h |
2 | c | f | i |
結果
まずは結果、後で過程を示す。
こんな感じでAggregateなしでもいけました。
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IEnumerable<T>> values){
return Enumerable.Range(0, values.Max(c => c.Count()))
.Select(i => values.Select(c => i < c.Count() ? c.ElementAt(i) : default(T)));
}
元のソース
@IganinTea さんのエントリー
LINQで入れ子になっているListの並びの縦横を入れ替える
中の@yuba さんのコメント欄のやつ
var resultList = targetList.Aggregate(
Enumerable.Range(0, targetList.Max(row => row.Count))
.Select(i => new List<string>())
,
(ll, row) => ll.Select((l, i) => { l.Add(row.Count <= i ? "" : row[i]); return l; })
)
.ToList();
}
1段階目
とりあえず真似る.
見通しが良くなるように3項演算子を外して、中身を改行する。
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IEnumerable<T>> values){
return values.Aggregate(
Enumerable.Range(0, values.Max(v => v.Count())).Select(i => new List<T>()),
(ll, row) => ll.Select((l, i) =>
{
l.Add(row.ElementAt(i));
return l;
}));
}
2段階目
columnを事前に求めてみる
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IEnumerable<T>> values){
var column = values.Max(v => v.Count());
return values.Aggregate(
Enumerable.Range(0, column).Select(i => new List<T>()),
(ll, row) => ll.Select((l, i) =>
{
l.Add(row.ElementAt(i));
return l;
}));
}
3段階目
Aggregateの中身がColumnのListを作っていることと理解
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IEnumerable<T>> values){
var column = values.Max(v => v.Count());
return Enumerable.Range(0, column).Select((v, i) =>
{
var list = new List<T>();
foreach (var item in values)
{
list.Add(item.ElementAt(i));
}
return list;
});
}
4段階目
foreachをselect文で書き換え
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IEnumerable<T>> values){
var column = values.Max(v => v.Count());
return Enumerable.Range(0, column).Select((v, i) =>
{
return values.Select(v2 => v2.ElementAt(i));
});
}
5段階目
中のreturnを消す。
大体完成。
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IEnumerable<T>> values){
var column = values.Max(v => v.Count());
return Enumerable.Range(0, column).Select(i => values.Select(v => v.ElementAt(i)));
}
6段階目
さっきまでのはジャグ配列だと死ぬから修正。
3項演算子復活
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IEnumerable<T>> values){
var column = values.Max(v => v.Count());
return Enumerable.Range(0, column)
.Select(i => values.Select(v => i < v.Count() ? v.ElementAt(i) : default(T)));
}
7段階目
columnの計算を戻して1つの式内で完結させる
以上の手順で書換。
Aggregateは決まるとすごいけど解読が難しい。
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IEnumerable<T>> values){
return Enumerable.Range(0, values.Max(c => c.Count()))
.Select(i => values.Select(c => i < c.Count() ? c.ElementAt(i) : default(T)));
}
ちょっと改良 T[],Listを受けてみる
配列とリスト版を書いた。
理由はなんとなくElementAtがないやつを見たかったから。
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<T[]> values)
{
return Enumerable.Range(0, values.Max(c => c.Length)).Select(i => values.Select(c => i < c.Length ? c[i] : default(T)));
}
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<List<T>> values)
{
return Enumerable.Range(0, values.Max(c => c.Count)).Select(i => values.Select(c => i < c.Count ? c[i] : default(T)));
}
よくよく定義を見てみたらArrayもListもIListを継承しているからまとめられた
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IList<T>> values)
{
return Enumerable.Range(0, values.Max(c => c.Count))
.Select(i => values.Select(c => i < c.Count ? c[i] : default(T)));
}
さらに改良 空き場所にdefault(T)以外を入れてみる。
参照型を渡すと毎回参照がコピーされて1つ変更されると全部変わるからそれはできないようにする。
1つが値型に制限する。
もう1つはデリゲートで毎回作るようにして参照のコピーを作らないようにできるようにする。
var tValues = Do(values,()=>new Class());
↓みたいに参照の値を渡したら意味はないけど。
var c = new Class();
var tValues = Do(values,()=>c);
まずはIEnumerableを受けるやつ。
//参照型だと後で変更したときにほかの部分も変わるから値型のみ受け付ける。
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IEnumerable<T>> values, T blankValue)
where T : struct
{
return Enumerable.Range(0, values.Max(c => c.Count()))
.Select(i => values.Select(c => i < c.Count() ? c.ElementAt(i) : blankValue));
}
//毎回newとかさせれば上記の問題が解消できるからFuncデリゲート入れてラムダ式とかでいけるようにする。
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IEnumerable<T>> values, Func<T> blankValue)
{
return Enumerable.Range(0, values.Max(c => c.Count()))
.Select(i => values.Select(c => i < c.Count() ? c.ElementAt(i) : blankValue()));
}
//使い方例
var tValues = Do(values, () => new T());
同じ感じでIListで受けるやつ
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IList<T>> values, T blankValue)
where T : struct
{
return Enumerable.Range(0, values.Max(c => c.Count))
.Select(i => values.Select(c => i < c.Count ? c[i] : blankValue));
}
public IEnumerable<IEnumerable<T>> Do<T>(IEnumerable<IList<T>> values, Func<T> blankValue)
{
return Enumerable.Range(0, values.Max(c => c.Count))
.Select(i => values.Select(c => i < c.Count ? c[i] : blankValue()));
}
速度的なあれは調べていません。
気が向いたら考えます。