เลือกหลายเขตข้อมูลจากรายการใน Linq


128

ใน ASP.NET C # ฉันมีโครงสร้าง:

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

และฉันมีรายชื่อเหล่านั้น ฉันต้องการที่จะเลือกcategory_idและcategory_nameวิ่งDISTINCTและสุดท้ายในORDERBYcategory_name

นี่คือสิ่งที่ฉันมีตอนนี้:

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

เห็นได้ชัดว่านี่เป็นเพียงชื่อหมวดหมู่ คำถามของฉันคือฉันจะได้หลายฟิลด์ได้อย่างไรและฉันจะจัดเก็บข้อมูลนี้ในโครงสร้างใด (ไม่ใช่กstring[])

แก้ไข

การใช้รายการโครงสร้างไม่ได้กำหนดไว้ในหิน หากจะแนะนำให้เปลี่ยนโครงสร้างข้อมูลสำรองของฉันเพื่อให้การเลือกง่ายขึ้น (ฉันจะเขียนสิ่งเหล่านี้เป็นจำนวนมาก) ฉันยินดีรับคำแนะนำ


10
แม้ว่าจะไม่เกี่ยวข้องกับฝั่ง LINQ แต่ฉันขอแนะนำอย่างยิ่งว่าอย่าใช้โครงสร้างที่ไม่แน่นอนหรือฟิลด์สาธารณะ โดยส่วนตัวแล้วฉันไม่ค่อยสร้างโครงสร้างในตอนแรก แต่โครงสร้างที่เปลี่ยนแปลงไม่ได้กำลังถามหาปัญหา
Jon Skeet

@Jon Skeet ขอบคุณ ฉันจะแปลงเป็นชั้นเรียนปกติกับสมาชิกส่วนตัว
เจตน์

1
@Midhat: โครงสร้างที่ไม่แน่นอนทำให้เกิดปัญหาทุกประเภทเนื่องจากพวกเขาไม่ได้ประพฤติตามที่ผู้คนคาดหวัง และพื้นที่สาธารณะก็ขาดการห่อหุ้มอย่างสมบูรณ์
Jon Skeet

@ จอนสเก็ต. คุณสามารถเจาะจงมากขึ้นกับข้อผิดพลาดของโครงสร้างที่ไม่แน่นอนหรือชี้ให้ฉันอ่าน
กลางเดือน

2
@Midhat: ดูที่stackoverflow.com/questions/441309/why-are-mutable-structs-evilสำหรับจุดเริ่มต้น
Jon Skeet

คำตอบ:


229

ประเภทที่ไม่ระบุตัวตนช่วยให้คุณสามารถเลือกฟิลด์ที่กำหนดเองลงในโครงสร้างข้อมูลที่พิมพ์อย่างรุนแรงในภายหลังในรหัสของคุณ:

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

เนื่องจากคุณ (เห็นได้ชัด) จำเป็นต้องจัดเก็บไว้เพื่อใช้ในภายหลังคุณสามารถใช้ตัวดำเนินการ GroupBy:

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

ฉันต้องการใช้ความแตกต่างใน 1 คอลัมน์และดึงข้อมูลหลายคอลัมน์ดังนั้นฉันจะทำได้อย่างไร?
Kishan Gajjar

1
ฉันไม่เคยคิดที่จะSelectเป็นประเภทใหม่ ในกรณีของฉันฉันเลือกเป็นไฟล์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);

แก้ไข : ทำให้เป็น LINQ-ey มากขึ้น!


ฮ่า ๆ ขอบคุณจริงๆฉันขอขอบคุณสำหรับความช่วยเหลือ เห็นได้ชัดว่านี่ไม่ใช่คำถามที่ยากที่สุดในโลก
เจตน์

@IRBMe ขอโทษนะคำถามเล็ก ๆ 'มูลค่า' คืออะไร?
Fernando S.Kroes

23

คุณสามารถใช้ประเภทที่ไม่ระบุตัวตน:

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

คอมไพเลอร์จะสร้างโค้ดสำหรับคลาสที่มีnameและcategory_nameคุณสมบัติและส่งคืนอินสแตนซ์ของคลาสนั้น คุณยังสามารถระบุชื่อคุณสมบัติด้วยตนเอง:

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

คุณสามารถมีคุณสมบัติตามจำนวนที่กำหนดได้


6

คุณสามารถเลือกหลายช่องโดยใช้ linq Select ดังที่แสดงไว้ด้านบนในตัวอย่างต่างๆซึ่งจะส่งคืนเป็นประเภทไม่ระบุตัวตน หากคุณต้องการหลีกเลี่ยงประเภทที่ไม่ระบุตัวตนนี่คือเคล็ดลับง่ายๆ

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

ฉันคิดว่าวิธีนี้ช่วยแก้ปัญหาของคุณได้


5

นี่เป็นงานที่ประเภทที่ไม่ระบุตัวตนเหมาะอย่างยิ่ง คุณสามารถส่งคืนอ็อบเจ็กต์ประเภทที่คอมไพเลอร์สร้างขึ้นโดยอัตโนมัติโดยอนุมานจากการใช้งาน

ไวยากรณ์อยู่ในรูปแบบนี้:

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

สำหรับกรณีของคุณให้ลองทำสิ่งต่อไปนี้:

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

สิ่งนี้ใช้ชนิดที่ไม่ระบุตัวตนดังนั้นคุณต้องใช้คีย์เวิร์ด var เนื่องจากไม่ทราบชนิดผลลัพธ์ของนิพจน์ล่วงหน้า



3

คุณสามารถทำให้เป็น KeyValuePair ดังนั้นมันจะส่งคืนไฟล์ "IEnumerable<KeyValuePair<string, string>>"

ดังนั้นมันจะเป็นดังนี้:

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

สิ่งนี้เพิ่มความซับซ้อนโดยไม่จำเป็นโดยไม่เกิดประโยชน์
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);
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.