[C#] linq를 사용하고 중복에 대해 걱정하지 않고 목록을 사전으로 변환

Person 객체 목록이 있습니다. 키가 성과 이름 (연결)이고 값이 Person 객체 인 Dictionary로 변환하고 싶습니다.

문제는 중복 된 사람들이 있다는 것이므로이 코드를 사용하면 문제가 발생합니다.

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

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

나는 그것이 이상하게 들린다는 것을 알고 있지만 실제로는 중복 이름에 관심이 없습니다. 이름이 여러 개인 경우 하나만 잡고 싶습니다. 어쨌든이 코드를 작성할 수 있습니까? 그래서 이름 중 하나만 사용하고 복제본을 날려 버리지 않습니다.



답변

linq가 아닌 명백한 솔루션은 다음과 같습니다.

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


답변

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;
}


답변

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의 솔루션보다 좋은지 모르겠지만 잘 작동합니다.


답변

이것은 람다 식과 함께 작동해야합니다.

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


답변

당신은 또한 사용할 수 있습니다 ToLookup당신은 다음 사전과 거의 같은 의미로 사용할 수 있습니다 LINQ의 기능을.

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

이것은 본질적으로 LukeH의 답변 에서 GroupBy를 수행 하지만 Dictionary가 제공하는 해싱을 제공합니다. 따라서 사전으로 변환 할 필요는 없지만 First키 값에 액세스해야 할 때마다 LINQ 함수를 사용하십시오 .


답변

중복을 허용한다는 차이점을 가지고 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;
    }

이 경우 중복이 있으면 마지막 값이 이깁니다.


답변

중복 제거를 처리하려면 메소드 IEqualityComparer<Person>에서 사용할 수 있는 것을 구현 한 Distinct()다음 사전을 얻는 것이 쉬울 것입니다. 주어진:

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