Question Distinct () de LINQ sur une propriété particulière


Je joue avec LINQ pour en savoir plus, mais je n'arrive pas à comprendre comment utiliser Distinct quand je n'ai pas de liste simple (une simple liste d'entiers est assez facile à faire, ce n'est pas la question). Ce que je veux utiliser Distinct sur une liste d'un objet sur un ou plus propriétés de l'objet?

Exemple: Si un objet est Person, avec propriété Id. Comment puis-je obtenir toute la personne et l'utilisation Distinct sur eux avec la propriété Id de l'objet?

Person1: Id=1, Name="Test1"
Person2: Id=1, Name="Test1"
Person3: Id=2, Name="Test2"

Comment puis-je obtenir juste Person1 et Person3? Est-ce possible?

Si ce n'est pas possible avec LINQ, quelle serait la meilleure façon d'avoir une liste de Person en fonction de certaines de ses propriétés dans .NET 3.5?


775
2018-01-28 20:45


origine


Réponses:


MODIFIER: Cela fait maintenant partie de PlusLINQ.

Ce dont vous avez besoin est un "distinct par" efficacement. Je ne crois pas que cela fasse partie de LINQ, même si c'est assez facile à écrire:

public static IEnumerable<TSource> DistinctBy<TSource, TKey>
    (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    HashSet<TKey> seenKeys = new HashSet<TKey>();
    foreach (TSource element in source)
    {
        if (seenKeys.Add(keySelector(element)))
        {
            yield return element;
        }
    }
}

Donc, pour trouver les valeurs distinctes en utilisant seulement le Id propriété, vous pouvez utiliser:

var query = people.DistinctBy(p => p.Id);

Et pour utiliser plusieurs propriétés, vous pouvez utiliser des types anonymes, qui implémentent l'égalité de manière appropriée:

var query = people.DistinctBy(p => new { p.Id, p.Name });

Non testé, mais il devrait fonctionner (et maintenant il compile au moins).

Il suppose que le code par défaut pour les clés - si vous voulez passer dans un comparateur d'égalité, il suffit de le transmettre au HashSet constructeur.


925
2018-01-28 21:17



Que faire si je veux obtenir une liste distincte basée sur un ou plus Propriétés?

Simple! Vous voulez les regrouper et choisir un gagnant hors du groupe.

List<Person> distinctPeople = allPeople
  .GroupBy(p => p.PersonId)
  .Select(g => g.First())
  .ToList();

Si vous voulez définir des groupes sur plusieurs propriétés, voici comment:

List<Person> distinctPeople = allPeople
  .GroupBy(p => new {p.PersonId, p.FavoriteColor} )
  .Select(g => g.First())
  .ToList();

1456
2018-01-29 14:39



Vous pouvez également utiliser la syntaxe de requête si vous voulez qu'elle ressemble à tout LINQ:

var uniquePeople = from p in people
                   group p by new {p.ID} //or group by new {p.ID, p.Name, p.Whatever}
                   into mygroup
                   select mygroup.FirstOrDefault();

61
2018-03-06 18:28



Je pense que c'est assez:

list.Select(s => s.MyField).Distinct();

47
2018-01-23 14:54



Utilisation:

List<Person> pList = new List<Person>();
/* Fill list */

var result = pList.Where(p => p.Name != null).GroupBy(p => p.Id).Select(grp => grp.FirstorDefault());

le where vous aide à filtrer les entrées (pourrait être plus complexe) et le groupby et select effectuer la fonction distincte.


36
2018-02-14 12:52



Vous pouvez le faire avec la norme Linq.ToLookup(). Cela va créer une collection de valeurs pour chaque clé unique. Sélectionnez simplement le premier élément de la collection

Persons.ToLookup(p => p.Id).Select(coll => coll.First());

18
2018-01-20 15:01



Le code suivant est fonctionnellement équivalent à La réponse de Jon Skeet.

Testé sur .NET 4.5, devrait fonctionner sur n'importe quelle version antérieure de LINQ.

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
  this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
  HashSet<TKey> seenKeys = new HashSet<TKey>();
  return source.Where(element => seenKeys.Add(keySelector(element)));
}

Incidentiellement, consultez La dernière version de Jon Skeet de DistinctBy.cs sur Google Code.


15
2018-02-06 19:56