私の質問はこれに似ています:Linqを使用してリスト内の連続するアイテムを検索します。ただし、ギャップのない最後の連続アイテムを取得したいのですが。例えば:
2, 4, 7, 8
出力
7,8
もう一つの例:
4,5,8,10,11,12
出力
10,11,12
どうすればそれができますか?
私の質問はこれに似ています:Linqを使用してリスト内の連続するアイテムを検索します。ただし、ギャップのない最後の連続アイテムを取得したいのですが。例えば:
2, 4, 7, 8
出力
7,8
もう一つの例:
4,5,8,10,11,12
出力
10,11,12
どうすればそれができますか?
私はあなたが複数のメンバーを持つ最後の連続したシーケンスが欲しいと仮定しています...だからシーケンスから
{4, 5, 8, 10, 11, 12, 15}
あなたはシーケンスを期待しています:
{10, 11, 12}
最後のシーケンスに単一のメンバーのみを含めることが許可されている場合に削除する行を指定し、次のシーケンスを指定しました。
{15}
これがlinqです:
new[] {4, 5, 8, 10, 11, 12, 15}
.Select((n,i) => new {n, i})
.GroupBy(x => x.n - x.i) //this line will group consecutive nums in the seq
.Where(g => g.Count() > 1) //remove this line if the seq {15} is expected
.Select(x => x.Select(xx => xx.n))
.LastOrDefault()
ここには、シーケンスの番号が昇順であるという隠された仮定があります。そうでない場合は、シーケンス内の連続するアイテムを検索するためのMicrosoftの拡張メソッドの機能を登録する必要があります。その場合はお知らせください。
これは機能し、この場合はLINQよりもおそらく簡単で効率的です。
var list = new[] { 2, 4, 7, 8 };
List<int> lastConsecutive = new List<int>();
for (int i = list.Length - 1; i > 0; i--)
{
lastConsecutive.Add(list[i]);
if (list[i] - 1 != list[i - 1])
break;
if(i==1 && list[i] - 1 == list[i - 1]) // needed since we're iterating just until 1
lastConsecutive.Add(list[0]);
}
lastConsecutive.Reverse();
これは遅くて言葉遣いの両方だと思いますが、これはおそらく、LINQを使用しているここでの最速の方法です。
テストリスト:
var list1 = new List<int> {2,4,7,8};
var list2 = new List<int> {4,5,8,10,11,12,15};
方法:
public List<int> LastConsecutive(List<int> list)
{
var rev = list.AsEnumerable().Reverse();
var res = rev.Zip(rev.Skip(1), (l, r) => new { left = l, right = r, diff = (l - r) })
.SkipWhile(x => x.diff != 1)
.TakeWhile(x => x.diff == 1);
return res.Take(1).Select(x => x.left)
.Concat(res.Select(x => x.right))
.Reverse().ToList();
}
これは後ろから前に移動し、要素をペアごとにチェックし、要素が連続し始めたとき(SkipWhile
)から連続しなくなるまで()だけを取得しますTakeWhile
。
次に、関連するペアワイズ番号(「元の」リストから左の番号、次にすべての右の番号)を引き出し、元に戻します。命令型バージョンと同様の効率ですが、私の意見では、LINQのおかげで読みやすくなっています。