C# 使用linq将列表转换为字典,不必担心重复

C# 使用linq将列表转换为字典,不必担心重复,c#,linq,dictionary,C#,Linq,Dictionary,我有一个Person对象的列表。我想转换成一个字典,其中键是名字和姓氏(连接),值是Person对象 问题是我有一些重复的人,所以如果我使用以下代码,这会爆炸: private Dictionary<string, Person> _people = new Dictionary<string, Person>(); _people = personList.ToDictionary( e => e.FirstandLastName, String

我有一个Person对象的列表。我想转换成一个字典,其中键是名字和姓氏(连接),值是Person对象

问题是我有一些重复的人,所以如果我使用以下代码,这会爆炸:

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

_people = personList.ToDictionary(
    e => e.FirstandLastName,
    StringComparer.OrdinalIgnoreCase);
private Dictionary\u people=new Dictionary();
_people=个人主义者(
e=>e.FirstandLastName,
普通木糖酶);
我知道这听起来很奇怪,但我现在真的不在乎重复的名字。如果有多个名字,我只想抓住一个。我是否可以编写上面的代码,这样它就只需要其中一个名称,而不会重复出现?

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;
}
//使用列表中的第一个值
var_people=新字典(StringComparer.OrdinalIgnoreCase);
foreach(个人列表中的var p)
{
如果(!_people.ContainsKey(p.FirstandLastName))
_人物[p.FirstandLastName]=p;
}
//使用列表中的最后一个值
var_people=新字典(StringComparer.OrdinalIgnoreCase);
foreach(个人列表中的var p)
{
_人物[p.FirstandLastName]=p;
}

这是一个显而易见的非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);
foreach(个人列表中的var个人)
{
如果(!myDictionary.ContainsKey(person.FirstAndLastName))
myDictionary.Add(person.FirstAndLastName,person);
}
如果您不介意总是添加最后一个,可以避免这样的双重查找:

// 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;
}
foreach(个人列表中的var个人)
{
myDictionary[person.FirstAndLastName]=person;
}

要处理消除重复,请实现一个可在
Distinct()
方法中使用的
IEqualityComparer
,然后获取词典就很容易了。 鉴于:

class PersonComparer:IEqualityComparer
{
公共布尔等于(人x,人y)
{
返回x.FirstAndLastName.Equals(y.FirstAndLastName,StringComparison.OrdinalIgnoreCase);
}
public int GetHashCode(Person obj)
{
返回obj.FirstAndLastName.ToUpper().GetHashCode();
}
}
班主任
{
公共字符串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);
List people=新列表()
{
new Person(){FirstAndLastName=“Bob Sanders”},
new Person(){FirstAndLastName=“Bob Sanders”},
新人(){FirstAndLastName=“Jane Thomas”}
};
字典=
people.Distinct(newpersoncomparer()).ToDictionary(p=>p.FirstAndLastName,p=>p);

使用Distinct()和且无分组的Linq解决方案是:

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

我不知道它是否比LukeH的解决方案更好,但它也能工作。

这应该适用于lambda表达式:

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

您可以创建一个类似于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; 
    }
公共静态字典安全字典(
这是一个数不清的来源,
Func键选择器,
Func元素选择器,
IEqualityComparer比较器=空)
{
var字典=新字典(比较器);
if(source==null)
{
返回字典;
}
foreach(源中的TSource元素)
{
字典[键选择器(元素)]=元素选择器(元素);
}
返回字典;
}

在这种情况下,如果存在重复项,则最后一个值获胜。

您还可以使用
ToLookup
LINQ函数,然后可以与字典互换使用

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

这将基本上完成中的GroupBy,但将提供字典提供的哈希。因此,您可能不需要将其转换为字典,只要在需要访问密钥值时使用LINQ
First
函数即可。

从Carra的解决方案开始,您还可以将其编写为:

foreach(var person in personList.Where(el => !myDictionary.ContainsKey(el.FirstAndLastName)))
{
    myDictionary.Add(person.FirstAndLastName, person);
}
DataTable DT=newdatatable();
添加(“第一”,类型(字符串));
添加(“第二”,类型(字符串));
添加(“ss”、“test1”);
添加(“sss”、“test2”);
添加(“系统”、“测试3”);
添加(“ss”、“test4”);
添加(“ss”、“test5”);
添加(“sts”、“test6”);
var dr=DT.AsEnumerable().GroupBy(S=>S.Field(“first”))。选择(S=>S.first()。
选择(S=>newkeyvaluepair(S.Field(“第一”)、S.Field(“第二”))。
ToDictionary(S=>S.Key,T=>T.Value);
foreach(dr中的var项)
{
Console.WriteLine(item.Key+“-”+item.Value);
}

如果我们想要返回词典中的所有人(而不是一个人),我们可以:

var _people = personList
.GroupBy(p => p.FirstandLastName)
.ToDictionary(g => g.Key, g => g.Select(x=>x));

大多数其他答案的问题在于,它们使用了
Distinct
GroupBy
ToLookup
,这在引擎盖下创建了一个额外的字典。同样,ToUpper会创建额外的字符串。 这就是我所做的,这几乎是微软代码的精确副本,除了一个变化:

    public static Dictionary<TKey, TSource> ToDictionaryIgnoreDup<TSource, TKey>
        (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer = null) =>
        source.ToDictionaryIgnoreDup(keySelector, i => i, comparer);

    public static Dictionary<TKey, TElement> ToDictionaryIgnoreDup<TSource, TKey, TElement>
        (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer = null)
    {
        if (keySelector == null)
            throw new ArgumentNullException(nameof(keySelector));
        if (elementSelector == null)
            throw new ArgumentNullException(nameof(elementSelector));
        var d = new Dictionary<TKey, TElement>(comparer ?? EqualityComparer<TKey>.Default);
        foreach (var element in source)
            d[keySelector(element)] = elementSelector(element);
        return d;
    }
publicstaticdictionary-ToDictionaryIgnoreDup
(此IEnumerable源、Func键选择器、IEqualityComparer比较器=null)=>
ToDictionaryIgnoreDup(keySelector,i=>i,comparer);
公共静态字典ToDictionaryIgnoreDup
(此IEnumerable源、Func键选择器、Func元素选择器、IEqualityComparer比较器=null)
{
if(keySelector==null)
抛出新ArgumentNullException(nameof(keySelector));
if(elementSelector==null)
抛出新ArgumentNullException(nameof(elementSelector));
var d=新字典(比较器??相等比较器.Default);
foreach(源中的var元素)
d[键选择器(元素)]=元素选择器(元素);
返回d;
}
因为索引器上的一个集合导致它添加键,所以它不会抛出,并且只执行一个键查找。你可以做一个