var q = from t in dict
from v in t.Value.elements
select new { name = t.Key, element = v };
Phương pháp ở đây là Enumerable.SelectMany. Sử dụng cú pháp phương pháp khuyến nông:
var q = dict.SelectMany(t => t.Value.elements.Select(v => new { name = t.Key, element = v }));
EDIT
Lưu ý rằng bạn cũng có thể sử dụng t.Value.name
trên, thay vì t.Key
, vì những giá trị đều bình đẳng.
Vì vậy, những gì đang xảy ra ở đây?
Cú pháp truy vấn-hiểu có lẽ là dễ hiểu nhất; bạn có thể viết một khối lặp tương đương để xem những gì đang xảy ra. Chúng ta không thể làm điều đó chỉ đơn giản với một loại vô danh, tuy nhiên, vì vậy chúng tôi sẽ công bố một loại trở lại:
class NameElement
{
public string name { get; set; }
public string element { get; set; }
}
IEnumerable<NameElement> GetResults(Dictionary<string, MyStruct> dict)
{
foreach (KeyValuePair<string, MyStruct> t in dict)
foreach (string v in t.Value.elements)
yield return new NameElement { name = t.Key, element = v };
}
Làm thế nào về cú pháp phương pháp khuyến nông (hoặc, là những gì thực sự xảy ra ở đây)?
(Điều này được lấy cảm hứng từ một phần qua đường bưu điện Eric Lippert tại https://stackoverflow.com/a/2704795/385844; Tôi đã có một lời giải thích phức tạp hơn nhiều, sau đó tôi đọc đó, và đến với điều này :)
Hãy nói rằng chúng ta muốn tránh khai báo NameElement kiểu. Chúng ta có thể sử dụng một kiểu ẩn danh bằng cách truyền vào một hàm.Chúng tôi muốn thay đổi cuộc gọi từ này:
var q = GetResults(dict);
này:
var q = GetResults(dict, (string1, string2) => new { name = string1, element = string2 });
Biểu thức lambda (string1, string2) => new { name = string1, element = string2 }
đại diện cho một chức năng mà mất 2 dây - được xác định bởi danh sách đối số (string1, string2)
- và trả về một thể hiện của loại ẩn danh được khởi tạo bằng các chuỗi đó - được xác định bởi biểu thức new { name = string1, element = string2 }
.
Việc thực hiện tương ứng là:
IEnumerable<T> GetResults<T>(
IEnumerable<KeyValuePair<string, MyStruct>> pairs,
Func<string, string, T> resultSelector)
{
foreach (KeyValuePair<string, MyStruct> pair in pairs)
foreach (string e in pair.Value.elements)
yield return resultSelector.Invoke(t.Key, v);
}
Loại suy luận cho phép chúng ta gọi hàm này mà không chỉ định T
theo tên. Đó là tiện dụng, bởi vì (theo như chúng ta biết là lập trình viên C#), kiểu chúng ta đang sử dụng không có tên: nó là vô danh.
Lưu ý rằng biến t
tại là pair
, để tránh nhầm lẫn với các tham số kiểu T
, và v
tại là e
, vì "yếu tố". Chúng tôi cũng đã thay đổi loại tham số đầu tiên thành một trong các loại cơ sở của nó, IEnumerable<KeyValuePair<string, MyStruct>>
. It's wordier, nhưng nó làm cho phương pháp hữu ích hơn, và nó sẽ rất hữu ích cuối cùng. Vì loại không còn là loại từ điển, chúng tôi cũng đã thay đổi tên của thông số từ dict
thành pairs
.
Chúng tôi có thể khái quát hóa điều này thêm. Thứ hai foreach
có tác dụng chiếu cặp khóa-giá trị vào một chuỗi kiểu T. Toàn bộ hiệu ứng đó có thể được đóng gói trong một hàm duy nhất; loại đại biểu sẽ là Func<KeyValuePair<string, MyStruct>, T>
. Bước đầu tiên là để cấu trúc lại phương pháp này vì vậy chúng tôi có một tuyên bố đơn có thể chuyển đổi các yếu tố pair
vào một chuỗi, bằng cách sử dụng phương pháp Select
để gọi resultSelector
đại biểu:
IEnumerable<T> GetResults<T>(
IEnumerable<KeyValuePair<string, MyStruct>> pairs,
Func<string, string, T> resultSelector)
{
foreach (KeyValuePair<string, MyStruct> pair in pairs)
foreach (T result in pair.Value.elements.Select(e => resultSelector.Invoke(pair.Key, e))
yield return result;
}
Bây giờ chúng ta có thể dễ dàng thay đổi chữ ký:
IEnumerable<T> GetResults<T>(
IEnumerable<KeyValuePair<string, MyStruct>> pairs,
Func<KeyValuePair<string, MyStruct>, IEnumerable<T>> resultSelector)
{
foreach (KeyValuePair<string, MyStruct> pair in pairs)
foreach (T result in resultSelector.Invoke(pair))
yield return result;
}
Trang web gọi bây giờ trông giống như sau; nhận thấy như thế nào biểu thức lambda hiện nay kết hợp logic mà chúng ta loại bỏ ra khỏi cơ thể phương pháp khi chúng ta thay đổi chữ ký của mình:
var q = GetResults(dict, pair => pair.Value.elements.Select(e => new { name = pair.Key, element = e }));
Để thực hiện phương pháp này hữu dụng hơn (và thực hiện nó ít tiết), chúng ta hãy thay thế các loại KeyValuePair<string, MyStruct>
với một nhập thông số, TSource
. Chúng tôi sẽ thay đổi một số tên khác cùng một lúc:
T -> TResult
pairs -> sourceSequence
pair -> sourceElement
Và, chỉ cần cho đá, chúng tôi sẽ làm cho nó trở thành một phương pháp khuyến nông:
static IEnumerable<TResult> GetResults<TSource, TResult>(
this IEnumerable<TSource> sourceSequence,
Func<TSource, IEnumerable<TResult>> resultSelector)
{
foreach (TSource sourceElement in sourceSequence)
foreach (T result in resultSelector.Invoke(pair))
yield return result;
}
Và có bạn có nó: SelectMany! Vâng, chức năng vẫn còn có tên sai, và việc thực hiện thực tế bao gồm xác nhận rằng chuỗi nguồn và chức năng chọn là không null, nhưng đó là logic cốt lõi.
Từ MSDN: SelectMany
"chiếu từng phần tử của chuỗi lên IE và làm phẳng chuỗi kết quả thành một chuỗi."
@sinanakyazici câu hỏi không xác định rằng đầu ra phải được lưu trữ trong từ điển (và thực sự, khi bạn lưu ý chính xác, trong c chú thích). – phoog
@phoog bạn nói đúng. Tôi hiểu lầm. Vì vậy, tôi đã xóa nhận xét của mình. – sinanakyazici
@sinanakyazici vì lý do nào đó tôi không thể xóa (cũng không chỉnh sửa) bình luận của mình trên trình duyệt của điện thoại: ( – phoog