web-dev-qa-db-de.com

Konvertieren Sie die Liste mit linq in ein Wörterbuch und machen Sie sich keine Gedanken über Duplikate

Ich habe eine Liste von Personenobjekten. Ich möchte in ein Wörterbuch konvertieren, bei dem der Schlüssel der Vor- und Nachname (verkettet) ist und der Wert das Objekt Person ist.

Das Problem ist, dass ich einige doppelte Personen habe. Wenn ich diesen Code verwende, bricht dies in die Luft:

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

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

Ich weiß, dass es sich komisch anhört, aber ich kümmere mich im Moment nicht wirklich um doppelte Namen. Wenn es mehrere Namen gibt, möchte ich einfach einen nehmen. Gibt es überhaupt einen Code, den ich oben schreiben kann, damit er nur einen der Namen annimmt und keine Duplikate sprengt?

124
leora

Hier ist die naheliegende, nicht linq-Lösung:

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

LINQ-Lösung:

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

Wenn Sie eine Nicht-LINQ-Lösung bevorzugen, können Sie Folgendes tun:

// 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;
}
360
LukeH

Eine Linq-Lösung mit Distinct () und ohne Gruppierung lautet:

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

Ich weiß nicht, ob es schöner ist als LukeHs Lösung, aber es funktioniert auch.

37
Tillito

Dies sollte mit dem Lambda-Ausdruck funktionieren:

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

Sie können auch die ToLookup LINQ-Funktion verwenden, die Sie dann fast austauschbar mit einem Wörterbuch verwenden können.

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

Dies wird im Wesentlichen die GroupBy in die Antwort von LukeH erledigen, aber das Hashing, das ein Dictionary bietet, geben. Daher müssen Sie es wahrscheinlich nicht in ein Wörterbuch konvertieren, sondern verwenden Sie einfach die LINQ-Funktion First, wenn Sie auf den Wert des Schlüssels zugreifen müssen.

9
palswim

Implementieren Sie zur Beseitigung von Duplikaten einen IEqualityComparer<Person>, der in der Distinct()-Methode verwendet werden kann.

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

Holen Sie sich Ihr Wörterbuch:

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

Sie können eine Erweiterungsmethode ähnlich ToDictionary () erstellen, mit dem Unterschied, dass Duplikate zulässig sind. So etwas wie:

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

In diesem Fall gewinnt der letzte Wert, wenn Duplikate vorhanden sind.

5
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

Falls wir die gesamte Person (statt nur einer Person) im zurückgegebenen Wörterbuch wünschen, könnten wir:

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

Ausgehend von Carras Lösung können Sie es auch schreiben als:

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