1

問題: int のリストがあり、2 回以上存在する数値を取得したい。

List<int> firstList = new List<int> { 1, 1, 3 };

期待される結果:

{ 1 }

これはLINQで簡単に実行できます..たとえば、これ

var result = firstList.Where(c => firstList.Count(d => c == d) > 1).Distinct();

問題は、これが複数の反復を行うことです。通常の for ループでは、O(N) の時間に到達できます。

List<int> result = new List<int>();
HashSet<int> doubles = new HashSet<int>();
foreach (var v in firstList)
{
    if (!doubles.Contains(v))
        doubles.Add(v);
    else
        result.Add(v);
}

これが linq aswel でやりたいことです...

HashSet<int> doubles = new HashSet<int>();
var result = firstList.Where((c) => doubles.Contains(c) ? true : !doubles.Add(c)).ToList();

これは私が考えることができる唯一の方法です..

質問: LINQ 内で "新しい HashSet" を宣言する方法はありますか? 私は次のようなことを考えていfirstList.Aggregate((c, d = new HashSet<int>) =>ます..

4

3 に答える 3

3

1 つの簡単なアプローチは次のとおりです。

var repeated = list.GroupBy(x => x)
                   .Where(g => g.Count() > 1)
                   .Select(g => g.Key);

これは 1 回だけ繰り返されます。手作りのソリューションよりも効率はわずかに低下しますが、かなり妥当なはずです。非常に簡単です。

于 2013-03-01T08:06:14.460 に答える
2

Evelie、ErenとJohnの両方の答えは正しく、それらはあなたが得ることができる最も簡単なものです。LINQの「pretty」構文には、let何かを紹介できるキーワードがありますが、ほとんどの場合var hashset、Erenの投稿に見られるのと同様の方法でコンパイラーによって書き直されます。ソースを「非表示」にできないのと同じようにfirstList、通常、他のサポート変数を非表示にすることはできません。少なくとも、まともな方法で

非常識な方法が存在します。非常識とは、はるかに読みにくく、曖昧であることを意味します。

たとえば、変数を非表示にしてErenの例を書き直してみましょう。

var firstList = new[] { 1, 1, 3 };

var result = Enumerable.Repeat(new { list = firstList, hash = new HashSet<int>() }, 1)
                .Select(anon => anon.list.Where(x => !anon.hash.Add(x)))
                .SelectMany(_ => _);

しかし、それは価値がありましたか?

また、標準のLINQ演算子に限定しないでください。あなたは簡単にあなた自身を紹介するかもしれません:

public static class MyOps
{
    public static IEnumerable<T> FindDuplicates<T>(this IEnumerable<T> input)
    {
        var hashSet = new HashSet<T>();
        foreach (var item in input)
            if (!hashSet.Add(item))
                yield return item;
    }
}

var firstList = new[] { 1, 1, 3 };

var result1 = firstList.FindDuplicates();

そして、それは通常、それを新しい拡張機能にラップするための小さな努力の価値があります。このコードはすべて、あなたや他の人が提示した以前のコードとほとんど同じであることに注意してください。これは、「変数ハイダー」または「拡張機能」のいずれかに「適切にラップ」されています。

編集:はい、そうです、ハッシュセットを含むすべての例はすべての重複を返します。区別する代わりに、2つのハッシュセットでそれを行うことができます。1つは重複チェック用で、もう1つは重複結果のフィルタリング用です。

public static class MyOps
{
    public static IEnumerable<T> FindDuplicates<T>(this IEnumerable<T> input)
    {
        var hashSet1 = new HashSet<T>();
        var hashSet2 = new HashSet<T>();
        foreach (var item in input)
            if (!hashSet1.Add(item)) // note the negation
                if (hashSet2.Add(item)) // note NO negation
                    yield return item;
    }
}

var firstList = new[] { 1, 1, 3 };

var result1 = firstList.FindDuplicates();

しかし、それはほとんど.distinct()がとにかく行うことです。

于 2013-03-01T08:47:47.093 に答える
1

技術的には、これを行うことができます(あなたがしていることのより短い方法です):

var hashSet = new HashSet<int>();
var filtered = firstList
    .Where(x =>
    {
        if (hashSet.Contains(x)) return true;
        hashSet.Add(x);
        return false;
    });

しかし、そのような副作用を回避し、上記のJon Skeetの方法を使用するのが最善だと思います(安全に上にあると仮定して:))

編集:

以下の Jon Skeet のコメントによると、これは次のように短縮することもできます。

var hashSet = new HashSet<int>();
var filtered = firstList.Where(x => !hashSet.Add(x));

これを一度使用する場合は注意が必要です。例えば:

var list1 = filtered.ToList(); // correct result
var list2 = filtered.ToList(); // incorrect result (returns all numbers)
于 2013-03-01T08:17:37.430 に答える