linq-Abfrage, um unterschiedliche Feldwerte aus einer Liste von Objekten zurückzugeben


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

Angenommen, es gibt eine Liste mit 100 Elementen von obj, aber nur 10 eindeutigen Typ-IDs.
Ist es möglich, eine LINQ-Abfrage zu schreiben, die die 10 eindeutigen Ints aus der Liste der Objekte zurückgibt?


Diese Frage ist so einfach und passt nicht für Kopfgeld.
Denker

Antworten:


152
objList.Select(o=>o.typeId).Distinct()

2
Die zweite Form scheint eine Überladung von Distinct zu verwenden, die meines Wissens nicht existiert.
Jon Skeet

Hoppla, entfernte den zweiten, danke @Jon Skeet, der mit IEqualityComparer
Arsen Mkrtchyan

3
Wie Jon Skeet erwähnt hat, werden nur die in Select angegebenen Eigenschaften zurückgegeben.
Peet vd Westhuizen

56

Angenommen, Sie möchten das vollständige Objekt, möchten sich aber nur mit der Unterscheidbarkeit befassen typeID, dann ist in LINQ nichts eingebaut, um dies zu vereinfachen. (Wenn Sie nur die typeIDWerte wollen , ist es einfach - projizieren Sie mit Selectund verwenden Sie dann den normalen DistinctAufruf.)

In MoreLINQ haben wir den DistinctByOperator, den Sie verwenden können:

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

Dies funktioniert jedoch nur für LINQ to Objects.

Sie können eine Gruppierung oder eine Suche verwenden, es ist nur etwas nervig und ineffizient:

var distinct = list.GroupBy(x => x.typeID, (key, group) => group.First());

Damit DistinctBy
angezeigt wird,

1
@Shil: Nein, ich habe über das DistinctBy in MoreLINQ geschrieben. Nichts mit Microsoft.Ajax.Utilities zu tun.
Jon Skeet

Jetzt kann ich sehen, dass es in LINQ eine Überladung von Distinct gibt, die einen IEqualityComparer als Parameter verwendet und eine Liste unterschiedlicher Objekte zurückgibt, abhängig von der Implementierung der Methoden in IEqualityComparer
Dipendu Paul,

1
@DipenduPaul: Ja, aber das bedeutet immer noch, einen Gleichheitsvergleich für eine bestimmte Eigenschaft zu erstellen, was ärgerlich ist und das Lesen erschwert. Wenn Sie die MoreLINQ-Abhängigkeit übernehmen können, ist das meiner Meinung nach sauberer.
Jon Skeet

20

Wenn Sie nur reines Linq verwenden möchten, können Sie groupby verwenden:

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

Wenn Sie möchten, dass eine Methode in der gesamten App verwendet wird, ähnlich wie bei 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;
        }
    }
}

Wenn Sie diese Methode verwenden, um die unterschiedlichen Werte nur mit der Id-Eigenschaft zu ermitteln, können Sie Folgendes verwenden:

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

Sie können mehrere Eigenschaften verwenden:

var query = objs.DistinctBy(p => new { p.TypeId, p.Name });

Vielen Dank für den Teil mit mehreren Eigenschaften !
Nae

7

Sicher verwenden Enumerable.Distinct.

Bei einer Sammlung von obj(z. B. foo) würden Sie so etwas tun:

var distinctTypeIDs = foo.Select(x => x.typeID).Distinct();

4

Ich denke, das ist es, wonach Sie suchen:

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

Ähnlich wie bei der Rückgabe eines bestimmten IQueryable mit LINQ?


.Firstist in Ordnung, da Sie die Gruppe nicht hätten, wenn nichts drin wäre.
mqp

1
Sie können eine alternative Überladung von verwenden GroupBy, um dies ebenfalls zu vereinfachen - siehe meine Antwort für ein Beispiel.
Jon Skeet

4

Wenn Sie nur Linq verwenden möchten, können Sie die Methoden Equals und GetHashCode überschreiben .

Produktklasse :

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

Haupt Methode:

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

1

Ich wollte bestimmte Daten an Dropdown-Listen binden und sie sollten eindeutig sein. Ich habe folgendes gemacht:

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

Sehen Sie, ob es hilft

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.