web-dev-qa-db-ja.com

linqを使用して、重複を心配せずにリストを辞書に変換します

Personオブジェクトのリストがあります。キーが姓と名(連結)であり、値がPersonオブジェクトである辞書に変換したい。

問題は、重複した人が何人かいるということです。このコードを使用すると、これが爆発します。

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

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

奇妙に聞こえるかもしれませんが、今のところ重複名についてはあまり気にしません。複数の名前がある場合は、1つだけを取得します。とにかく私は上記のコードを書くことができますので、名前の1つだけを取り、重複で爆発しませんか?

136
leora

明らかな非linqソリューションは次のとおりです。

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

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

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のソリューションよりも優れているかどうかはわかりませんが、同様に機能します。

38
Tillito

これはラムダ式で動作するはずです:

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

ToLookup LINQ関数を使用することもできます。この関数は、辞書とほぼ同じ意味で使用できます。

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

これは基本的に LukeHの答え でGroupByを行いますが、辞書が提供するハッシュを提供します。したがって、おそらく辞書に変換する必要はありませんが、キーの値にアクセスする必要があるときは常にLINQ First関数を使用するだけです。

11
palswim

重複の除去を処理するには、Distinct()メソッドで使用できるIEqualityComparer<Person>を実装すると、辞書を簡単に取得できます。与えられた:

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

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

この場合、重複がある場合、最後の値が優先されます。

6
Eric
        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);
        }
2
King

返される辞書にすべてのPerson(1人のPersonではなく)が必要な場合は、次のようにします。

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

Carraのソリューションから始めて、次のように書くこともできます。

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