4

次のような列挙可能なソースがあるとしましょう。

IEnumerable<string> source = new [] { "first", "first", "first", "second" };

これを返すLINQステートメントを構築できるようにしたい:

"first", "first", "second"

最初のうちの 1 つだけがなくなったことに注意してください。私の場合、3つの「最初の」すべてが等しいと見なされるため、どれを気にしません。私は試しましsource.Except(new [] { "first" })たが、それはすべてのインスタンスを取り除きます。

4

5 に答える 5

10
source
  .GroupBy(s => s)
  .SelectMany(g => g.Skip(1).DefaultIfEmpty(g.First()))

グループごとに、グループの最初の要素をスキップして残りを返します - それが何も返さない場合を除き...その場合、グループの最初の要素を返します。


source
  .GroupBy(s => s)
  .SelectMany(g => g.Take(1).Concat(g.Skip(2)))

グループごとに、最初の要素を取得し、3 番目の要素から取得します - 常に 2 番目の要素をスキップします。

于 2012-05-22T21:09:41.627 に答える
8

Amy Bの答えはあなたに本当に近いと思いますが、そこに値が1つしかない場合、値は削除されません。これは、元のポスターが探していたものだと思います.

要求された項目の単一のインスタンスを、それが最後のインスタンスであっても削除する拡張メソッドを次に示します。これは LINQ Except() 呼び出しを反映していますが、すべてのインスタンスではなく、最初のインスタンスのみを削除します。

    public static IEnumerable<T> ExceptSingle<T>(this IEnumerable<T> source, T valueToRemove)
    {
        return source
            .GroupBy(s => s)
            .SelectMany(g => g.Key.Equals(valueToRemove) ? g.Skip(1) : g);
    }

与えられた:{"one", "two", "three", "three", "three"}
呼び出しのsource.ExceptSingle("three")結果は{"one", "two", "three", "three"}

与えられた:{"one", "two", "three", "three"}
呼び出しのsource.ExceptSingle("three")結果は{"one", "two", "three"}

与えられた:{"one", "two", "three"}
呼び出しのsource.ExceptSingle("three")結果は{"one", "two"}

与えられた:{"one", "two", "three", "three"}
呼び出しのsource.ExceptSingle("four")結果は{"one", "two", "three", "three"}

于 2012-08-15T20:08:18.753 に答える
3

私はそれを行うためのワンライナーLINQステートメントを見つけました。別のフラグ変数が必要です。私はそれを拡張メソッドとして実装しました:

public static IEnumerable<T> ExceptOne<T>(this IEnumerable<T> enumerable, T element)
{
    var i = 0;

    return enumerable.Where(original => !EqualityComparer<T>.Default.Equals(original, element) || ++i > 1);
}

後で「numberToRemove」パラメーターを追加したい場合に備えて、intを使用しました(>1を>numberToRemoveに変更します)。YAGNIとそのすべてですが、とにかくブール値と同じくらい読みやすくなりました。

于 2012-05-23T12:05:11.843 に答える
1
IEnumerable<string> source = new [] { "first", "first", "first", "second" };

List<string> newSource = new List<string>();

var foo = source.GroupBy (s => s).Select (s => new KeyValuePair<string,int>(s.Key, (s.Count()>1)?s.Count()-1:s.Count ()));

foreach (var element in foo)
{
    newSource.AddRange(Enumerable.Repeat(element.Key,element.Value));
}

ここで非常に迅速な努力。基本的に、これにより、元のリストから各個別のキーとインスタンスの数を含む2番目のリストが作成され、複数ある場合は1つ差し引かれ、適切な数の要素でリストが再レンダリングされます。

デビッドBの答えほどエレガントではありませんが、私はすでに書いているので、別の可能な答えとして投稿したほうがよいでしょう。ForeachをLinqステートメントに組み込むことができると確信していますが、遅く、私の脳は機能していません。

于 2012-05-22T21:17:37.737 に答える
0

私は LINQ にあまり詳しくありませんが、これに使用する一般的なフローは次のとおりです。

すべての一意のアイテムを新しいリスト B に保存します。

A: {1, 1, 1, 2, 4, 4, 6}

になる

B: {1, 2, 4, 6}

B を反復処理し、A のインスタンスが存在する場合は削除します。つまり、次のようになります。

A: {1, 1, 1, 2, 4, 4, 6}

になる

F: {1, 1, 2, 4, 6}

お役に立てれば!

于 2012-05-22T21:10:07.100 に答える