2010-07-23 89 views
105

我有一個Person對象列表。我想轉換爲一個字典,其中的鍵是名和姓(連接),值是Person對象。使用LINQ將列表轉換爲字典而不用擔心重複項

的問題是,我有一些重複的人,所以這個打擊,如果我用這個代碼:

private Dictionary<string, Person> _people = new Dictionary<string, Person>(); 

_people = personList.ToDictionary(
    e => e.FirstandLastName, 
    StringComparer.OrdinalIgnoreCase); 

我知道這聽起來很奇怪,但我現在真的不關心重複的名字。如果有多個名字我只想抓一個。無論如何,我可以在上面寫這個代碼,所以它只需要一個名稱,並且不會重複複製?

+1

的副本(基於密鑰),我不知道,如果你想保持他們或失去他們?保留它們將需要一個'Dictionary >'(或等價物)。 – 2010-07-23 14:22:16

+0

@Anony Pegram - 只想保留其中一個。我更新了問題更加明確 – leora 2010-07-23 14:25:28

回答

41

這裏是顯而易見的,非LINQ的解決方案:

foreach(var person in personList) 
{ 
    if(!myDictionary.Keys.Contains(person.FirstAndLastName)) 
    myDictionary.Add(person.FirstAndLastName, person); 
} 
+143

多數民衆贊成在2007年:) – leora 2010-07-23 14:29:26

+2

,不會忽略案例 – onof 2010-07-23 14:32:39

+0

是的,關於時間我們從工作中的.NET 2.0框架更新... @onof不完全難以忽視的情況。只需以大寫形式添加所有密鑰。 – Carra 2010-07-23 14:42:37

305

LINQ的解決方案:

// Use the first value in group 
var _people = personList 
    .GroupBy(p => p.FirstandLastName, StringComparer.OrdinalIgnoreCase) 
    .ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase); 

// Use the last value in group 
var _people = personList 
    .GroupBy(p => p.FirstandLastName, StringComparer.OrdinalIgnoreCase) 
    .ToDictionary(g => g.Key, g => g.Last(), StringComparer.OrdinalIgnoreCase); 

如果你喜歡一個非LINQ的解決方案,那麼你可以做這樣的事情:

// Use the first value in list 
var _people = new Dictionary<string, Person>(StringComparer.OrdinalIgnoreCase); 
foreach (var p in personList) 
{ 
    if (!_people.ContainsKey(p.FirstandLastName)) 
     _people[p.FirstandLastName] = p; 
} 

// Use the last value in list 
var _people = new Dictionary<string, Person>(StringComparer.OrdinalIgnoreCase); 
foreach (var p in personList) 
{ 
    _people[p.FirstandLastName] = p; 
} 
+1

+1非常優雅(我會盡快投票 - 今天沒有更多投票:)) – onof 2010-07-23 14:34:43

+5

@LukeH小注:您的兩個片段不是等價的:LINQ變體保留第一個元素,即非LINQ片段保留最後一個元素? – toong 2013-09-25 14:09:45

+4

@toong:這是真的,絕對值得注意。 (儘管在這種情況下,OP似乎並不在意它們最終結束了哪個元素。) – LukeH 2013-09-25 17:01:22

7

要處理消除重複項,請實施可在中使用的方法,然後讓你的字典變得容易。 考慮:

class PersonComparer : IEqualityComparer<Person> 
{ 
    public bool Equals(Person x, Person y) 
    { 
     return x.FirstAndLastName.Equals(y.FirstAndLastName, StringComparison.OrdinalIgnoreCase); 
    } 

    public int GetHashCode(Person obj) 
    { 
     return obj.FirstAndLastName.ToUpper().GetHashCode(); 
    } 
} 

class Person 
{ 
    public string FirstAndLastName { get; set; } 
} 

讓你的詞典:

List<Person> people = new List<Person>() 
{ 
    new Person() { FirstAndLastName = "Bob Sanders" }, 
    new Person() { FirstAndLastName = "Bob Sanders" }, 
    new Person() { FirstAndLastName = "Jane Thomas" } 
}; 

Dictionary<string, Person> dictionary = 
    people.Distinct(new PersonComparer()).ToDictionary(p => p.FirstAndLastName, p => p); 
34

使用LINQ的解決方案鮮明的(),並沒有分組是:

var _people = personList 
    .Select(item => new { Key = item.Key, FirstAndLastName = item.FirstAndLastName }) 
    .Distinct() 
    .ToDictionary(item => item.Key, item => item.FirstFirstAndLastName, StringComparer.OrdinalIgnoreCase); 

我不知道這是否是比LukeH的解決方案更好,但它也可以工作。

+0

你確定這有效嗎? Distinct如何比較您創建的新參考類型?我認爲你需要將某種IEqualityComparer傳遞給Distinct才能按預期得到這項工作。 – 2014-07-25 15:28:02

+5

忽略我以前的評論。請參閱http://stackoverflow.com/questions/543482/linq-select-distinct-with-anonymous-types – 2014-07-25 15:29:46

+0

如果你想覆蓋如何截然不同,請檢查http://stackoverflow.com/questions/489258/linqs-不同於特定屬性 – 2016-05-19 22:21:10

20

這應該與lambda表達式工作:

personList.Distinct().ToDictionary(i => i.FirstandLastName, i => i); 
+2

它必須是: 'personList.Distinct()。ToDictionary(i => i.FirstandLastName,i => i);' – Gh61 2014-08-08 08:08:06

+0

這隻會在Person的默認IEqualityComparer按姓名比較,忽略大小寫。否則,編寫這樣一個IEqualityComparer並使用相關的重載過載。同樣,你的ToDIctionary方法應該採用不區分大小寫的比較器來匹配OP的要求。 – Joe 2016-11-23 12:54:06

5

可以創建類似於ToDictionary(),不同之處在於它允許重複的擴展方法。例如:

public static Dictionary<TKey, TElement> SafeToDictionary<TSource, TKey, TElement>(
     this IEnumerable<TSource> source, 
     Func<TSource, TKey> keySelector, 
     Func<TSource, TElement> elementSelector, 
     IEqualityComparer<TKey> comparer = null) 
    { 
     var dictionary = new Dictionary<TKey, TElement>(comparer); 

     if (source == null) 
     { 
      return dictionary; 
     } 

     foreach (TSource element in source) 
     { 
      dictionary[keySelector(element)] = elementSelector(element); 
     } 

     return dictionary; 
    } 

在這種情況下,如果有重複,則最後一個值勝。

7

您也可以使用ToLookup LINQ函數,然後您可以幾乎與字典交換使用。

_people = personList 
    .ToLookup(e => e.FirstandLastName, StringComparer.OrdinalIgnoreCase); 
_people.ToDictionary(kl => kl.Key, kl => kl.First()); // Potentially unnecessary 

這將基本上做的GroupBy在LukeH's answer,但會給予一個解釋提供了散列。因此,您可能不需要將其轉換爲字典,但只要您需要訪問密鑰的值,只需使用LINQ First函數即可。

0

從卡拉格的解決方案開始,你也可以寫爲:

foreach(var person in personList.Where(el => !myDictionary.ContainsKey(el.FirstAndLastName))) 
{ 
    myDictionary.Add(person.FirstAndLastName, person); 
} 
+0

不是說任何人都會試圖使用它,但不要試圖使用它。在迭代它們時修改集合是一個壞主意。 – kidmosey 2018-01-11 13:28:10

2
 DataTable DT = new DataTable(); 
     DT.Columns.Add("first", typeof(string)); 
     DT.Columns.Add("second", typeof(string)); 

     DT.Rows.Add("ss", "test1"); 
     DT.Rows.Add("sss", "test2"); 
     DT.Rows.Add("sys", "test3"); 
     DT.Rows.Add("ss", "test4"); 
     DT.Rows.Add("ss", "test5"); 
     DT.Rows.Add("sts", "test6"); 

     var dr = DT.AsEnumerable().GroupBy(S => S.Field<string>("first")).Select(S => S.First()). 
      Select(S => new KeyValuePair<string, string>(S.Field<string>("first"), S.Field<string>("second"))). 
      ToDictionary(S => S.Key, T => T.Value); 

     foreach (var item in dr) 
     { 
      Console.WriteLine(item.Key + "-" + item.Value); 
     } 
+0

我建議你通過閱讀[最小,完整和可驗證的示例](http://stackoverflow.com/help/mcve)來改進您的示例。 – IlGala 2016-04-27 12:36:15