requête linq pour renvoyer des valeurs de champ distinctes à partir d'une liste d'objets

88
class obj
{
    int typeId; //10 types  0-9 
    string uniqueString; //this is unique
}

Supposons qu'il existe une liste avec 100 éléments d'obj, mais seulement 10 ID de type uniques.
Est-il possible d'écrire une requête LINQ renvoyant les 10 entiers uniques de la liste des objs?

patrick
la source
Cette question est si simple et ne convient pas à la prime.
Thinker

Réponses:

155
objList.Select(o=>o.typeId).Distinct()
Arsen Mkrtchyan
la source
2
La deuxième forme semble utiliser une surcharge de Distinct qui n'existe pas à ma connaissance.
Jon Skeet
Oups, supprimé le deuxième, merci @Jon Skeet, cela peut être fait avec IEqualityComparer
Arsen Mkrtchyan
3
Comme Jon Skeet l'a mentionné, cela ne renverra que les propriétés spécifiées dans Select.
Peet vd Westhuizen
58

En supposant que vous vouliez l'objet complet, mais que vous ne vouliez traiter que de la distinction typeID, rien n'est intégré à LINQ pour vous faciliter la tâche. (Si vous voulez juste les typeIDvaleurs, c'est facile - projetez-y avec Select, puis utilisez l' Distinctappel normal .)

Dans MoreLINQ, nous avons l' DistinctByopérateur que vous pouvez utiliser:

var distinct = list.DistinctBy(x => x.typeID);

Cela ne fonctionne que pour LINQ to Objects.

Vous pouvez utiliser un regroupement ou une recherche, c'est juste un peu ennuyeux et inefficace:

var distinct = list.GroupBy(x => x.typeID, (key, group) => group.First());
Jon Skeet
la source
Pour que DistinctBy apparaisse, vous devez ajouter l'espace de noms Microsoft.Ajax.Utilities
Shiljo Paulson
1
@Shil: Non, j'écrivais sur le DistinctBy dans MoreLINQ. Rien à voir avec Microsoft.Ajax.Utilities.
Jon Skeet du
Maintenant, je peux voir qu'il y a une surcharge de Distinct dans LINQ qui prend un IEqualityComparer comme paramètre et renvoie une liste d'objets distincts en fonction de l'implémentation des méthodes dans IEqualityComparer
Dipendu Paul
1
@DipenduPaul: Oui, mais cela signifie quand même créer un comparateur d'égalité pour une propriété donnée, ce qui est ennuyeux et rend la lecture plus difficile. Si vous pouvez prendre la dépendance MoreLINQ, je pense que c'est plus propre.
Jon Skeet
22

Si vous souhaitez simplement utiliser Linq pur, vous pouvez utiliser groupby:

List<obj> distinct =
  objs.GroupBy(car => car.typeID).Select(g => g.First()).ToList();

Si vous souhaitez qu'une méthode soit utilisée dans toute l'application, similaire à ce que fait MoreLinq :

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.Contains(keySelector(element)))
        {
            seenKeys.Add(keySelector(element));
            yield return element;
        }
    }
}

En utilisant cette méthode pour trouver les valeurs distinctes en utilisant uniquement la propriété Id, vous pouvez utiliser:

var query = objs.DistinctBy(p => p.TypeId);

vous pouvez utiliser plusieurs propriétés:

var query = objs.DistinctBy(p => new { p.TypeId, p.Name });
JulioCT
la source
Merci pour la partie propriétés multiples !
Nae
7

Bien sûr, utilisez Enumerable.Distinct.

Étant donné une collection de obj(par exemple foo), vous feriez quelque chose comme ceci:

var distinctTypeIDs = foo.Select(x => x.typeID).Distinct();
Donut
la source
5

Je pense que c'est ce que vous recherchez:

    var objs= (from c in List_Objects 
orderby c.TypeID  select c).GroupBy(g=>g.TypeID).Select(x=>x.FirstOrDefault());      

Similaire à celui-ci Renvoyer un IQueryable distinct avec LINQ?

Jauge
la source
.Firstc'est bien, puisque vous n'auriez pas le groupe s'il n'y avait pas quelque chose dedans.
mqp
1
Vous pouvez également utiliser une surcharge alternative de GroupBypour rendre cela plus simple - voir ma réponse pour un exemple.
Jon Skeet
3

Si vous souhaitez simplement utiliser Linq, vous pouvez remplacer les méthodes Equals et GetHashCode .

Classe de produit :

public class Product
{
    public string ProductName { get; set; }
    public int Id { get; set; }


    public override bool Equals(object obj)
    {
        if (!(obj is Product))
        {
            return false;
        }

        var other = (Product)obj;
        return Id == other.Id;
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

Méthode principale :

static void Main(string[] args)
    {

        var products = new List<Product>
        {
            new Product{ ProductName="Product 1",Id = 1},
            new Product{ ProductName="Product 2",Id = 2},
            new Product{ ProductName="Product 4",Id = 5},
            new Product{ ProductName="Product 3",Id = 3},
            new Product{ ProductName="Product 4",Id = 4},
            new Product{ ProductName="Product 6",Id = 4},
            new Product{ ProductName="Product 6",Id = 4},
        };

        var itemsDistinctByProductName = products.Distinct().ToList();

        foreach (var product in itemsDistinctByProductName)
        {
            Console.WriteLine($"Product Id : {product.Id} ProductName : {product.ProductName} ");
        }

        Console.ReadKey();
    }
Reza Jenabi
la source
1

Je voulais lier une donnée particulière à la liste déroulante et elle devrait être distincte. J'ai fait ce qui suit:

List<ClassDetails> classDetails;
List<string> classDetailsData = classDetails.Select(dt => dt.Data).Distinct.ToList();
ddlData.DataSource = classDetailsData;
ddlData.Databind();

Voyez si ça aide

Charmy Vora
la source