Linq'teki Listeden Birden Fazla Alan Seçin


128

ASP.NET C # 'de bir yapım var:

public struct Data
{
    public int item1;
    public int item2;
    public int category_id;
    public string category_name;
}

ve bende bunların listesi var. Ben seçmek istiyorum category_idve category_namebir çalışan, DISTINCTve nihayet bir ORDERBYüstünde category_name.

İşte şimdi sahip olduğum şey:

List<Data> listObject = getData();
string[] catNames = listObject
                    .Select(i=> i.category_name)
                    .Distinct()
                    .OrderByDescending(s => s)
                    .ToArray();

Bu açıkça kategori adını alır. Sorum şu, birden çok alanı nasıl elde ederim ve bunu hangi veri yapısında saklayacağım (a değil string[])?

DÜZENLE

Bir yapı listesi kullanmak kesin değildir. Seçimleri kolaylaştırmak için destek veri yapımı değiştirmem tavsiye edilirse (bunlardan çok yazacağım), o zaman memnuniyetle öneriler alırdım.


10
LINQ tarafıyla alakasız olsa da, değiştirilebilir yapılar veya genel alanlar kullanmamanızı şiddetle tavsiye ederim. Şahsen ben nadiren ilk etapta yapılar yaratırım, ancak değiştirilebilir yapılar sadece sorun arıyor.
Jon Skeet

@Jon Skeet Teşekkürler. Özel üyelerle normal bir sınıfa dönüştüreceğim.
Chet

1
@Midhat: Değişken yapılar insanların beklediği gibi davranmadıkları için her türlü soruna neden olur. Ve kamusal alanlar tam bir kapsülleme eksikliği verir.
Jon Skeet

@Jon Skeet. Değiştirilebilir yapıların tuzakları konusunda daha spesifik olabilir misiniz veya beni bir okumaya yönlendirebilir misiniz?
Midhat

2
@Midhat: Başlangıç ​​noktası için stackoverflow.com/questions/441309/why-are-mutable-structs-evil'e bir göz atın .
Jon Skeet

Yanıtlar:


229

Anonim türler, kodunuzda daha sonra güçlü bir şekilde yazılan veri yapılarına rastgele alanlar seçmenize olanak tanır:

var cats = listObject
    .Select(i => new { i.category_id, i.category_name })
    .Distinct()
    .OrderByDescending(i => i.category_name)
    .ToArray();

Daha sonra kullanmak üzere saklamanız gerektiğinden (görünüşe göre), GroupBy işlecini kullanabilirsiniz:

Data[] cats = listObject
    .GroupBy(i => new { i.category_id, i.category_name })
    .OrderByDescending(g => g.Key.category_name)
    .Select(g => g.First())
    .ToArray();

1 sütun üzerinde farklı kullanmak ve birden fazla sütun almak istiyorum.ÖNasıl yapabilirim?
Kishan Gajjar

1
SelectYeni bir tip bulmayı hiç düşünmedim . Benim durumumda yeni bir KeyValuePair.
cjbarth

26
var selectedCategories =
    from value in
        (from data in listObject
        orderby data.category_name descending
        select new { ID = data.category_id, Name = data.category_name })
    group value by value.Name into g
    select g.First();

foreach (var category in selectedCategories) Console.WriteLine(category);

Düzenleme : Daha fazla LINQ-ey yaptı!


Haha, teşekkürler, yardımın için minnettarım. Görünüşe göre bu dünyadaki en zor soru değildi.
Chet

@IRBMe Affedersiniz, küçük soru. Tam olarak 'değer' nedir?
Fernando S. Kroes

23

Anonim bir tür kullanabilirsiniz:

.Select(i => new { i.name, i.category_name })

Derleyici bir sınıf için kod oluşturur nameve category_namebu sınıfın özellikleri ve döner durumda. Mülk adlarını manuel olarak da belirtebilirsiniz:

i => new { Id = i.category_id, Name = i.category_name }

İstediğiniz sayıda mülkünüz olabilir.


6

Yukarıda çeşitli örneklerde gösterildiği gibi linq Select'i kullanarak birden fazla alan seçebilirsiniz, bu bir Anonim Tür olarak dönecektir. Bu anonim türden kaçınmak istiyorsanız, işte basit bir numara.

var items = listObject.Select(f => new List<int>() { f.Item1, f.Item2 }).SelectMany(item => item).Distinct();

Sanırım bu senin problemini çözer


5

Bu, anonim türlerin çok uygun olduğu bir görevdir . Derleyici tarafından otomatik olarak oluşturulan ve kullanımdan çıkarılan bir türdeki nesneleri döndürebilirsiniz.

Sözdizimi şu biçimdedir:

new { Property1 = value1, Property2 = value2, ... }

Durumunuz için aşağıdaki gibi bir şey deneyin:

var listObject = getData();
var catNames = listObject.Select(i =>
    new { CatName = i.category_name, Item1 = i.item1, Item2 = i.item2 })
    .Distinct().OrderByDescending(s => s).ToArray();

4
var result = listObject.Select( i => new{ i.category_name, i.category_id } )

Bu, anonim türler kullanır, bu nedenle ifadenin ortaya çıkan türü önceden bilinmediğinden, var anahtar sözcüğünü kullanmanız gerekir.


3
(from i in list
 select new { i.category_id, i.category_name })
 .Distinct()
 .OrderBy(i => i.category_name);

3

Bunu bir KeyValuePair yapabilirsiniz, böylece bir "IEnumerable<KeyValuePair<string, string>>"

Yani şöyle olacak:

.Select(i => new KeyValuePair<string, string>(i.category_id, i.category_name )).Distinct();

Bu, bir fayda olmaksızın gereksiz karmaşıklık ekler
Matze

2
public class Student
{
    public string Name { set; get; }
    public int ID { set; get; }
}

class Program
{
  static void Main(string[] args)
    {
        Student[] students =
        {
        new Student { Name="zoyeb" , ID=1},
        new Student { Name="Siddiq" , ID=2},
        new Student { Name="sam" , ID=3},
        new Student { Name="james" , ID=4},
        new Student { Name="sonia" , ID=5}
        };

        var studentCollection = from s in students select new { s.ID , s.Name};

        foreach (var student in studentCollection)
        {
            Console.WriteLine(student.Name);
            Console.WriteLine(student.ID);
        }
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.