Saya memiliki class book yang memiliki properti IEnumerable<string> Tags:

public class Book
{
    public Book(string title, IEnumerable<string> tags)
        => (Title, Tags) = (title, tags);

    public string Title { get; }
    public IEnumerable<string> Tags { get; }
}

Sekarang saya memiliki IEnumerable<Book> books dan saya hanya perlu mendapatkan tag umum di antara SEMUA objek.

public static void TestMethod()
{
    var book1 = new Book("Book1", new List<string> { "c#", ".net", "programming" });
    var book2 = new Book("Book2", new List<string> { "ado", "c#" });
    var book3 = new Book("Book3", new List<string> { "programming", "c#" });
    var book4 = new Book("Book4", new List<string> { "ef-core", "database", ".net" });
    var book5 = new Book("Book5", new List<string> { ".net", "visual-studio", "c#", "programming" });

    var list1 = new List<Book> { book1, book2, book3 };
    var list2 = new List<Book> { book1, book3, book5 };
    var list3 = new List<Book> { book1, book4, book5 };
    var list4 = new List<Book> { book1, book2, book4 };


    // expected returns:
    // list1 => { "c#" }
    // list2 => { "c#", "programming" }
    // list3 => { ".net" }
    // list4 => { }

    Console.WriteLine($"list1 => {{{string.Join(';', GetCommonTags(list1))}}}");
    Console.WriteLine($"list2 => {{{string.Join(';', GetCommonTags(list2))}}}");
    Console.WriteLine($"list3 => {{{string.Join(';', GetCommonTags(list3))}}}");
    Console.WriteLine($"list4 => {{{string.Join(';', GetCommonTags(list4))}}}");
}

Saya benar-benar tidak tahu cara yang tepat untuk melakukan ini. Sampai saat itu, saya melakukannya dengan cara yang mengerikan ini (di Brasil, kami menyebutnya "gambiarra", atau "Solusi" dalam bahasa Inggris):

public static IEnumerable<string> GetCommonTags(IEnumerable<Book> books)
{
    var allTags = books.SelectMany(b => b.Tags).Distinct();
    var commonTags = new List<string>();

    foreach (var tag in allTags)
    {
        var isCommonTag = true;
        foreach (var book in books)
        {
            if (!book.Tags.Contains(tag))
            {
                isCommonTag = false;
                break;
            }
        }

        if (isCommonTag) commonTags.Add(tag);
    }

    return commonTags;
}

Ini berfungsi, tetapi bagaimana melakukannya dengan benar, atau mungkin dengan LINQ?

0
Max Dolabella 4 Januari 2021, 21:07

3 jawaban

Jawaban Terbaik

Anda dapat menggunakan operasi LINQ All

public static IEnumerable<string> GetCommonTags(IEnumerable<Book> books)
{
    var allTags = books.SelectMany(b => b.Tags).Distinct()
                     .Where(t => books.All(b => b.Tags.Contains(t)));
    return allTags;
}

Kode di atas pertama-tama mendapatkan semua Tags dan memvalidasi jika tag adalah bagian dari books collection menggunakan Semua yang menentukan apakah semua elemen urutan memenuhi suatu kondisi.

Anda dapat memeriksa biola ini https://dotnetfiddle.net/aHzyP0 yang menunjukkan skenario Anda.

0
user1672994 4 Januari 2021, 18:12

Dengan menggunakan metode Aggregate, Anda dapat Intersect di semua tag, menggunakan default(IEnumerable<string>) sebagai tanda untuk mengambil set pertama Tags:

public IEnumerable<string> GetCommonTags(IEnumerable<Book> books)
    => books.Aggregate(default(IEnumerable<string>),
                       (commonTags, book) => commonTags == default(IEnumerable<string>)
                                                ? book.Tags
                                                : commonTags.Intersect(book.Tags));
1
NetMage 4 Januari 2021, 18:20

HashSet<Tag> akan efisien di sini, dan hanya membutuhkan satu pass:

var result = new HashSet(b[0].Tags);   //Init with first book
for(var i = 1; i < b.Count; i++)
    result.IntersectWith(b[i].Tags);

Jika b adalah IEnumerable itu sedikit lebih rumit.

1
Charlieface 4 Januari 2021, 20:44