これが私の質問のシナリオです: 私は配列を持っています。
{ 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 }
結果は次のようになります (配列要素 => その数):
4 => 1
1 => 2
3 => 2
2 => 1
5 => 1
3 => 1
2 => 2
私はこれが によって達成できることを知っていますfor loop
。
しかし、LINQを使用してより少ないコード行を使用してこれを可能にするために、Googleは多くのことを成功させませんでした。
これを行うための最も最適な方法は、反復子ブロックを使用して「LINQ のような」拡張メソッドを作成することだと思います。これにより、データを 1 回パスして計算を実行できます。小さな数値配列で計算を実行したいだけであれば、パフォーマンスはまったく重要ではないことに注意してください。もちろん、これは偽装した for ループです。
static class Extensions {
public static IEnumerable<Tuple<T, Int32>> ToRunLengths<T>(this IEnumerable<T> source) {
using (var enumerator = source.GetEnumerator()) {
// Empty input leads to empty output.
if (!enumerator.MoveNext())
yield break;
// Retrieve first item of the sequence.
var currentValue = enumerator.Current;
var runLength = 1;
// Iterate the remaining items in the sequence.
while (enumerator.MoveNext()) {
var value = enumerator.Current;
if (!Equals(value, currentValue)) {
// A new run is starting. Return the previous run.
yield return Tuple.Create(currentValue, runLength);
currentValue = value;
runLength = 0;
}
runLength += 1;
}
// Return the last run.
yield return Tuple.Create(currentValue, runLength);
}
}
}
拡張メソッドはジェネリックであり、任意の型で使用できることに注意してください。を使用して、値が等しいかどうか比較されObject.Equals
ます。ただし、必要に応じて を渡して、IEqualityComparer<T>
値の比較方法をカスタマイズできます。
次のような方法を使用できます。
var numbers = new[] { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 };
var runLengths = numbers.ToRunLengths();
入力データの場合、結果は次のタプルになります。
4 1 1 2 3 2 2 1 5 1 3 1 2 2
これが機能するLINQ式です(編集:コードをもう少し引き締めます):
var data = new int[] { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 };
var result = data.Select ((item, index) =>
new
{
Key = item,
Count = (index == 0 || data.ElementAt(index - 1) != item)
? data.Skip(index).TakeWhile (d => d == item).Count ()
: -1
}
)
.Where (d => d.Count != -1);
そして、これが機能していることを示す証拠です。
(これにカウントされる私の削除されたものに対する2つの賛成票を避けるために別の答えを追加します...)
私はこれについて少し考えました (今、私は質問を理解しました) が、LINQ でこれをうまく行う方法が本当に明確ではありません。Zip
またはを使用する可能性があることは間違いありませんAggregate
が、それらは比較的不明確です。使い方foreach
はとても簡単です:
// Simplest way of building an empty list of an anonymous type...
var results = new[] { new { Value = 0, Count = 0 } }.Take(0).ToList();
// TODO: Handle empty arrays
int currentValue = array[0];
int currentCount = 1;
foreach (var value in array.Skip(1))
{
if (currentValue != value)
{
results.Add(new { Value = currentValue, Count = currentCount });
currentCount = 0;
currentValue = value;
}
currentCount++;
}
// Handle tail, which we won't have emitted yet
results.Add(new { Value = currentValue, Count = currentCount });
これは十分に短くありませんか?
public static IEnumerable<KeyValuePair<T, int>> Repeats<T>(
this IEnumerable<T> source)
{
int count = 0;
T lastItem = source.First();
foreach (var item in source)
{
if (Equals(item, lastItem))
{
count++;
}
else
{
yield return new KeyValuePair<T, int>(lastItem, count);
lastItem = item;
count = 1;
}
}
yield return new KeyValuePair<T, int>(lastItem, count);
}
linq の方法を見てみたいと思います。
私はあなたがそこに必要な方法をすでに書いた。呼び方は次のとおりです。
foreach(var g in numbers.GroupContiguous(i => i))
{
Console.WriteLine("{0} => {1}", g.Key, g.Count);
}
見よ (これを LINQPad で直接実行できます --rle
ここで魔法が起こります):
var xs = new[] { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 };
var rle = Enumerable.Range(0, xs.Length)
.Where(i => i == 0 || xs[i - 1] != xs[i])
.Select(i => new { Key = xs[i], Count = xs.Skip(i).TakeWhile(x => x == xs[i]).Count() });
Console.WriteLine(rle);
もちろん、これは O(n^2) ですが、仕様で線形効率を要求していません。
var array = new int[] {1,1,2,3,5,6,6 };
foreach (var g in array.GroupBy(i => i))
{
Console.WriteLine("{0} => {1}", g.Key, g.Count());
}
var array = new int[]{};//whatever ur array is
array.select((s)=>{return array.where((s2)=>{s == s2}).count();});
唯一の問題は、1〜2回の場合、1〜2回の結果が得られることです
var array = new int[] {1,1,2,3,5,6,6 };
var arrayd = array.Distinct();
var arrayl= arrayd.Select(s => { return array.Where(s2 => s2 == s).Count(); }).ToArray();
出力
arrayl=[0]2 [1]1 [2]1 [3]1 [4]2
試しGroupBy
てみてくださいList<int>
List<int> list = new List<int>() { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 };
var res = list.GroupBy(val => val);
foreach (var v in res)
{
MessageBox.Show(v.Key.ToString() + "=>" + v.Count().ToString());
}