Operasi proyeksi (C#)

Proyeksi mengacu pada operasi mengubah objek menjadi bentuk baru yang sering hanya terdiri dari properti tersebut yang kemudian digunakan. Dengan menggunakan proyeksi, Anda bisa membangun jenis baru yang dibangun dari setiap objek. Anda bisa memproyeksikan properti dan melakukan fungsi matematika di atasnya. Anda juga bisa memproyeksikan objek asli tanpa mengubahnya.

Metode operator kueri standar yang melakukan proyeksi tercantum di bagian berikut.

Metode

Nama metode Deskripsi Sintaks ekspresi kueri C# Informasi selengkapnya
Pilih Nilai proyek yang didasarkan pada fungsi transformasi. select Enumerable.Select
Queryable.Select
SelectMany Proyek urutan nilai yang didasarkan pada fungsi transformasi dan kemudian meratakannya menjadi satu urutan. Menggunakan beberapa klausul from Enumerable.SelectMany
Queryable.SelectMany
Kode Pos Menghasilkan urutan tuple dengan elemen dari 2-3 urutan yang ditentukan. Tidak berlaku. Enumerable.Zip
Queryable.Zip

Select

Contoh berikut menggunakan klausa select untuk memproyeksikan huruf pertama dari setiap string dalam daftar string.

List<string> words = ["an", "apple", "a", "day"];

var query = from word in words
            select word.Substring(0, 1);

foreach (string s in query)
{
    Console.WriteLine(s);
}

/* This code produces the following output:

    a
    a
    a
    d
*/

Kueri yang setara menggunakan sintaks metode diperlihatkan dalam kode berikut:

List<string> words = ["an", "apple", "a", "day"];

var query = words.Select(word => word.Substring(0, 1));

foreach (string s in query)
{
    Console.WriteLine(s);
}

/* This code produces the following output:

    a
    a
    a
    d
*/

SelectMany

Contoh berikut menggunakan beberapa klausul from untuk memproyeksikan setiap kata dari setiap string dalam daftar string.

List<string> phrases = ["an apple a day", "the quick brown fox"];

var query = from phrase in phrases
            from word in phrase.Split(' ')
            select word;

foreach (string s in query)
{
    Console.WriteLine(s);
}

/* This code produces the following output:

    an
    apple
    a
    day
    the
    quick
    brown
    fox
*/

Kueri yang setara menggunakan sintaks metode diperlihatkan dalam kode berikut:

List<string> phrases = ["an apple a day", "the quick brown fox"];

var query = phrases.SelectMany(phrases => phrases.Split(' '));

foreach (string s in query)
{
    Console.WriteLine(s);
}

/* This code produces the following output:

    an
    apple
    a
    day
    the
    quick
    brown
    fox
*/

Metode ini SelectMany juga dapat membentuk kombinasi pencocokan setiap item dalam urutan pertama dengan setiap item dalam urutan kedua:

var query = from number in numbers
            from letter in letters
            select (number, letter);

foreach (var item in query)
{
    Console.WriteLine(item);
}

Kueri yang setara menggunakan sintaks metode diperlihatkan dalam kode berikut:

var method = numbers
    .SelectMany(number => letters,
    (number, letter) => (number, letter));

foreach (var item in method)
{
    Console.WriteLine(item);
}

Zip

Ada beberapa kelebihan beban untuk operator proyeksi Zip. Semua metode Zip bekerja pada urutan dua atau lebih jenis heterogen yang memungkinkan. Dua kelebihan beban pertama mengembalikan tuple, dengan jenis penempatan yang sesuai dari urutan yang diberikan.

Pertimbangkan koleksi berikut:

// An int array with 7 elements.
IEnumerable<int> numbers = [1, 2, 3, 4, 5, 6, 7];
// A char array with 6 elements.
IEnumerable<char> letters = ['A', 'B', 'C', 'D', 'E', 'F'];

Untuk memproyeksikan urutan ini bersama-sama, gunakan operator Enumerable.Zip<TFirst,TSecond>(IEnumerable<TFirst>, IEnumerable<TSecond>):

foreach ((int number, char letter) in numbers.Zip(letters))
{
    Console.WriteLine($"Number: {number} zipped with letter: '{letter}'");
}
// This code produces the following output:
//     Number: 1 zipped with letter: 'A'
//     Number: 2 zipped with letter: 'B'
//     Number: 3 zipped with letter: 'C'
//     Number: 4 zipped with letter: 'D'
//     Number: 5 zipped with letter: 'E'
//     Number: 6 zipped with letter: 'F'

Penting

Urutan yang dihasilkan dari operasi zip tidak pernah lebih panjang dari urutan terpendek. Panjang koleksi numbers dan letters berbeda, dan urutan yang dihasilkan menghilangkan elemen terakhir dari koleksi numbers, karena tidak memiliki apa pun untuk di-zip.

Kelebihan beban kedua menerima urutan third. Mari kita buat koleksi lain, yaitu emoji:

// A string array with 8 elements.
IEnumerable<string> emoji = [ "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯"];

Untuk memproyeksikan urutan ini bersama-sama, gunakan operator Enumerable.Zip<TFirst,TSecond,TThird>(IEnumerable<TFirst>, IEnumerable<TSecond>, IEnumerable<TThird>):

foreach ((int number, char letter, string em) in numbers.Zip(letters, emoji))
{
    Console.WriteLine(
        $"Number: {number} is zipped with letter: '{letter}' and emoji: {em}");
}
// This code produces the following output:
//     Number: 1 is zipped with letter: 'A' and emoji: 🤓
//     Number: 2 is zipped with letter: 'B' and emoji: 🔥
//     Number: 3 is zipped with letter: 'C' and emoji: 🎉
//     Number: 4 is zipped with letter: 'D' and emoji: 👀
//     Number: 5 is zipped with letter: 'E' and emoji: ⭐
//     Number: 6 is zipped with letter: 'F' and emoji: 💜

Sama seperti kelebihan beban sebelumnya, metode Zip ini memproyeksikan tuple, tetapi kali ini dengan tiga elemen.

Kelebihan beban ketiga menerima argumen Func<TFirst, TSecond, TResult> yang bertindak sebagai pemilih hasil. Anda dapat memproyekikan urutan hasil baru dari urutan yang di-zip.

foreach (string result in
    numbers.Zip(letters, (number, letter) => $"{number} = {letter} ({(int)letter})"))
{
    Console.WriteLine(result);
}
// This code produces the following output:
//     1 = A (65)
//     2 = B (66)
//     3 = C (67)
//     4 = D (68)
//     5 = E (69)
//     6 = F (70)

Dengan kelebihan beban Zip sebelumnya, fungsi yang ditentukan diterapkan ke elemen numbers yang sesuai dan letter, menghasilkan urutan hasil string.

Select versus SelectMany

Pekerjaan Select dan SelectMany adalah menghasilkan nilai hasil (atau nilai) dari nilai sumber. Select menghasilkan satu nilai hasil untuk setiap nilai sumber. Oleh karena itu, hasil keseluruhan adalah koleksi yang memiliki jumlah elemen yang sama dengan koleksi sumber. Sebaliknya, SelectMany menghasilkan satu hasil keseluruhan yang berisi subkoleksi yang digabungkan dari setiap nilai sumber. Fungsi transformasi yang diteruskan sebagai argumen ke SelectMany harus mengembalikan urutan nilai yang bisa disebutkan untuk setiap nilai sumber. SelectMany menggabungkan urutan yang dapat dijumlahkan ini untuk membuat satu urutan besar.

Dua ilustrasi berikut menunjukkan perbedaan konseptual antara tindakan kedua metode ini. Dalam setiap kasus, asumsikan bahwa fungsi pemilih (transformasi) memilih array bunga dari setiap nilai sumber.

Ilustrasi ini menggambarkan cara Select mengembalikan koleksi yang memiliki jumlah elemen yang sama dengan kumpulan sumber.

Grafik yang memperlihatkan tindakan Pilih()

Ilustrasi ini menggambarkan cara SelectMany menggabungkan urutan menengah larik menjadi satu nilai hasil akhir yang berisi setiap nilai dari setiap larik menengah.

Grafik memperlihatkan tindakan SelectMany()

Contoh kode

Contoh berikut membandingkan perilaku Select dan SelectMany. Kode ini membuat "buket" bunga dengan mengambil item dari setiap daftar nama bunga dalam koleksi sumber. Dalam contoh berikut, "nilai tunggal" yang digunakan fungsi Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) transformasi adalah kumpulan nilai. Contoh ini memerlukan perulangan tambahan foreach untuk menghitung setiap string di setiap urutan berikutnya.

class Bouquet
{
    public required List<string> Flowers { get; init; }
}

static void SelectVsSelectMany()
{
    List<Bouquet> bouquets =
    [
        new Bouquet { Flowers = ["sunflower", "daisy", "daffodil", "larkspur"] },
        new Bouquet { Flowers = ["tulip", "rose", "orchid"] },
        new Bouquet { Flowers = ["gladiolis", "lily", "snapdragon", "aster", "protea"] },
        new Bouquet { Flowers = ["larkspur", "lilac", "iris", "dahlia"] }
    ];

    IEnumerable<List<string>> query1 = bouquets.Select(bq => bq.Flowers);

    IEnumerable<string> query2 = bouquets.SelectMany(bq => bq.Flowers);

    Console.WriteLine("Results by using Select():");
    // Note the extra foreach loop here.
    foreach (IEnumerable<string> collection in query1)
    {
        foreach (string item in collection)
        {
            Console.WriteLine(item);
        }
    }

    Console.WriteLine("\nResults by using SelectMany():");
    foreach (string item in query2)
    {
        Console.WriteLine(item);
    }
}

Lihat juga