2010-07-23 54 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