2013-03-01 72 views
1

問題: 我有一個int列表,我想獲取存在兩次或更多次的數字。用LINQ創建臨時工作變量?

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>) => ..

回答

2

Evelie,Eren和John的答案都是正確的,他們是最簡單的你可以得到的。在LINQ的'漂亮'語法中,有一個let關鍵字,它允許你引入一些東西,但是這大部分都是由編譯器重寫的,類似於Eren的帖子中看到的var hashset。就像你無法'隱藏'源firstList一樣,你通常不能隱藏其他支持變量。至少,在理智的方式

瘋狂的方式存在。瘋了我的意思是遠不那麼可讀和模糊。

例如,讓我們重寫與變量隱藏二連的例子:

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(); 

,這通常值得花費很小的努力把它包裝成一個新的擴展。請注意,所有這些代碼與您和其他人提供的代碼幾乎相同。它只是「很好地包裝」到「變量 - 隱藏者」或「擴展」中。

編輯:是的,這是真的,所有與哈希集的例子將返回所有重複。您可以通過兩個哈希集來實現它:一個用於重複檢查,另一個用於過濾重複結果。

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()會做反正。

3

一個簡單的方法是:

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

這隻會重複一次 - 這將是稍微比你手工製作的解決方案效率較低,但應該是相當合理的 - 和這很簡單。

+0

是的你是對的。我的意思是兩次或更多次。然而這也會是O(N)^ 2 ..這比O(N)場景更糟:( – Evelie 2013-03-01 08:13:44

+0

@Evelie:是什麼讓你認爲它會是O(N^2)?(組被實現,是O(1)。) – 2013-03-01 08:14:20

+0

GroupBy將迭代列表中的條目,然後你必須再次迭代它們(在每個組內)以計算它們......或者至少這是我的想法 – Evelie 2013-03-01 08:15:21

1

從技術上講,你可以(你在做什麼的只是一個較短的方式)做到這一點:

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

但我認爲這是最好的避免這樣的副作用,只是使用喬恩斯基特的上述方法(安全假設它的上面:))

編輯:

下面

每喬恩斯基特的評論,這甚至可以被縮短:

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) 
+0

是的,但即使在這裏,我必須在我的LINQ之外聲明HashSet :(。 – Evelie 2013-03-01 08:18:39

+1

你甚至不需要'if'或'Contains'檢查 - 你可以使用' .Where(x =>!hashSet.Add(x))' – 2013-03-01 08:20:58

+0

如果有3個1,我會在沒有ifcheck的結果中得到2,你將不得不使用一個明顯的after完成那個工作 – Evelie 2013-03-01 08:36:38