简体   繁体   中英

LINQ: Select a collection?

I'm trying to use LINQ:

IEnumerable<String> debtors = (from expense in CurrentExpenses
                               where expense.WhoPaid == username
                               select expense.WhoOwes.AsEnumerable()).Distinct();

( username and WhoPaid are strings, WhoOwes is ICollection<String> )

What I want to do is get an IEnumerable of, for each expense where username paid, all the people who owe for it. I'm not really sure how to do it. Here is the compiler error:

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<string>>' to 'System.Collections.Generic.IEnumerable<string>'. An explicit conversion exists (are you missing a cast?)

What is the correct syntax for this?

The code you've provided doesn't compile because you're trying to assign a sequence of a sequence of strings (each top-level sequence coming from a specific expense) to a reference that expects a sequence of strings.

Going by the return-type of your query, I assume you only need a sequence containing all the debtors for the user? If so, you need to flatten the result sequence.

// implicitly IEnumerable<string>
var debtors = CurrentExpenses.Where(expense => expense.WhoPaid == username)
                             .Select(expense => expense.WhoOwes)
                             .SelectMany(debtors => debtors) // flatten sequence
                             .Distinct();

(you can use a single SelectMany clause to do the projection and flattening together.)

or, in query syntax:

var debtors = (from expense in CurrentExpenses
               where expense.WhoPaid == username
               from debtor in expense.WhoOwes
               select debtor).Distinct();

If you need to find all expenses paid by the user and their associated debtors, you can stop at the first filter:

// implicitly IEnumerable<Expense>
var expensesPaidByUser = CurrentExpenses.Where(expense => expense.WhoPaid == username);

The debtors associated with each expense are already encapsulated in the Expense object, so you probably don't need a query more complicated than this; it's already effectively a sequence of a sequence of debtors grouped by expense.

If you do want a separate type to hold an expense and its associated debtors, you can do this with an anonymous type, although I don't see why its necessary in your case:

// implictly IEnumerable<anonymousType>
var debtorsByExpense = CurrentExpenses.Where(expense => expense.WhoPaid == username)
                                      .Select(expense => new { Expense = expense, Debtors = expense.WhoOwes });

Use:

var debtors = ...

instead of

IEnumerable<String> debtors = ...

The return type of LINQ expressions is often difficult to determine for the programmer. The var keyword tells the compiler to infer the type automatically, so you don't need to. Hover your mouse cursor over var in your IDE to see what the compiler has inferred.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM