J'ai une liste d'objets Personne. Je veux convertir en dictionnaire où la clé est le nom et le prénom (concaténés) et la valeur est l'objet Personne.
Le problème est que j'ai des personnes en double, alors ça explose si j'utilise ce code:
private Dictionary<string, Person> _people = new Dictionary<string, Person>();
_people = personList.ToDictionary(
e => e.FirstandLastName,
StringComparer.OrdinalIgnoreCase);
Je sais que ça a l'air bizarre mais je ne me soucie pas vraiment des noms en double pour l'instant. S'il y a plusieurs noms, je veux juste en prendre un. Y at-il de toute façon que je puisse écrire ce code ci-dessus pour qu'il prenne simplement l'un des noms et ne explose pas sur les doublons?
Voici la solution évidente, non linq:
foreach(var person in personList)
{
if(!myDictionary.Keys.Contains(person.FirstAndLastName))
myDictionary.Add(person.FirstAndLastName, person);
}
Solution 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);
Si vous préférez une solution non-LINQ, vous pouvez procéder comme suit:
// 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;
}
Une solution Linq utilisant Distinct () et sans groupement est:
var _people = personList
.Select(item => new { Key = item.Key, FirstAndLastName = item.FirstAndLastName })
.Distinct()
.ToDictionary(item => item.Key, item => item.FirstFirstAndLastName, StringComparer.OrdinalIgnoreCase);
Je ne sais pas si c'est mieux que la solution de LukeH mais ça marche aussi.
Cela devrait fonctionner avec l'expression lambda:
personList.Distinct().ToDictionary(i => i.FirstandLastName, i => i);
Vous pouvez également utiliser la fonction ToLookup
LINQ, que vous pouvez ensuite utiliser de façon presque interchangeable avec un dictionnaire.
_people = personList
.ToLookup(e => e.FirstandLastName, StringComparer.OrdinalIgnoreCase);
_people.ToDictionary(kl => kl.Key, kl => kl.First()); // Potentially unnecessary
Cela fera essentiellement GroupBy dans la réponse de LukeH , mais donnera le hachage fourni par un dictionnaire. Donc, vous n'avez probablement pas besoin de le convertir en dictionnaire, mais utilisez simplement la fonction LINQ First
chaque fois que vous devez accéder à la valeur de la clé.
Pour gérer l'élimination des doublons, implémentez un IEqualityComparer<Person>
qui peut être utilisé dans la méthode Distinct()
, puis obtenir votre dictionnaire sera facile ...
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; }
}
Obtenez votre dictionnaire:
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);
Vous pouvez créer une méthode d'extension similaire à ToDictionary (), à la différence qu'elle autorise les doublons. Quelque chose comme:
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;
}
Dans ce cas, s'il y a des doublons, la dernière valeur l'emporte.
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);
}
Si nous voulons que toute la personne (au lieu d’une seule personne) soit reprise dans le dictionnaire précédent, nous pourrions:
var _people = personList
.GroupBy(p => p.FirstandLastName)
.ToDictionary(g => g.Key, g => g.Select(x=>x));
À partir de la solution de Carra, vous pouvez également l'écrire comme suit:
foreach(var person in personList.Where(el => !myDictionary.ContainsKey(el.FirstAndLastName)))
{
myDictionary.Add(person.FirstAndLastName, person);
}