تهیه گزارشات Crosstab به کمک LINQ - قسمت دوم
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: دو دقیقه


اگر به قسمت اول «تهیه گزارشات Crosstab به کمک LINQ» دقت کرده باشید، یک مشکل کوچک دارد و آن هم لزوم مشخص سازی دقیق ستون‌هایی است که می‌خواهیم در گزارش ظاهر شوند. مثلا دقیقا مشخص کنیم که نام واحد چیست یا دقیقا روز را مشخص کنیم. این مورد برای گزارش‌های کوچک مشکلی ندارد؛ ولی اگر همان مثال دوم را در نظر گرفته و بازه را کمی بیشتر کنیم، مثلا یک ماه، آن وقت باید حداقل 30 بار بنویسیم Day1IsPresent تا ... Day30IsPresent و یا اگر بازه‌ی گزارشگیری به اختیار کاربر باشد آن وقت چه باید کرد؟ مثلا یکبار 7 روز پایان ماه را انتخاب کند، یکبار 14 روز را، شاید یک بار هم مثلا 90 روز را مد نظر داشته باشد (تعداد ستون‌ها متغیر باشد یا به عبارتی Dynamic Crosstab نیاز است ایجاد شود).
برای حل این مساله، می‌توان از متد الحاقی زیر از سایت extensionmethod.net کمک گرفت:

using System;
using System.Collections.Generic;
using System.Linq;

namespace PivotExtensions
{
public static class Ext
{
public static Dictionary<TKey1, Dictionary<TKey2, TValue>>
Pivot<TSource, TKey1, TKey2, TValue>
(
this IEnumerable<TSource> source,
Func<TSource, TKey1> key1Selector,
Func<TSource, TKey2> key2Selector,
Func<IEnumerable<TSource>, TValue> aggregate
)
{
return source.GroupBy(key1Selector)
.Select(
key1Group => new
{
Key = key1Group.Key,
Value = key1Group.GroupBy(key2Selector)
.Select(
key2Group => new
{
K = key2Group.Key,
V = aggregate(key2Group)
})
.ToDictionary(e => e.K, o => o.V)
})
.ToDictionary(e => e.Key, o => o.Value);
}
}
}

در این متد:
key1Selector مشخص کننده ستون‌های ثابت و مشخص سمت راست یا چپ (بر اساس جهت صفحه) گزارش است. در سیستم‌های مختلف این ستون‌ها نام‌هایی مانند keyColumn ، leftColumn و Row Heading ممکن است داشته باشند.
key2Selector ستون‌های پویای گزارش را تشکیل می‌دهد. در سایر سیستم‌ها این پارامتر، pivotNameColumn ،VariableColumn ، topField و یا Column Heading هم نامیده می‌شود.
Aggregate در اینجا مشخص می‌کند که مقادیر ستون‌های پویای یاد شده چگونه باید محاسبه شوند.

با توجه به این متد، برای نمونه جهت حل مثال اول قسمت قبل خواهیم داشت:

var list = ExpenseDataSource.ExpensesDataSource();
var pivotList = list.Pivot(
x =>
new
{
x.Date.Year,
x.Date.Month
},
x1 => x1.Department,
x2 => x2.Sum(x => x.Expenses));

با خروجی


فایل LINQPad آن از اینجا قابل دریافت است.


و برای حل مثال دوم قسمت قبل می‌توان نوشت:

var list2 = StudentsStatDataSource.CreateWeeklyReportDataSource();
var lst = list2.Pivot(
x =>
new
{
x.Id,
x.Name
},
x1 => "Day " + x1.Date.Day,
x2 => x2.First().IsPresent);

با خروجی


فایل LINQPad آن از اینجا قابل دریافت است.

  • #
    ‫۱۲ سال و ۱۲ ماه قبل، چهارشنبه ۲۵ آبان ۱۳۹۰، ساعت ۱۲:۵۹
    سلام آقای نصیری
    ممنون از مطلب مفیدتون.شما در اول این مطلب فرمودید :« اگر بازه‌ی گزارشگیری به اختیار کاربر باشد» اما مثالهایی که زدین رو باید از اول دونست که چه ستونهایی رو میخواهیم.منطورم اینه که فرض کنید که اگر ما یک چک لیست برای ماههای سال داشته باشیم که کاربر بتونه هر ترتیبی از 12 ماه رو انتخاب کنه چکار باید کرد؟ با تشکر
  • #
    ‫۱۲ سال و ۱۲ ماه قبل، چهارشنبه ۲۵ آبان ۱۳۹۰، ساعت ۱۳:۵۲
    سلام،
    نه؛ ما اینجا هم نمی‌دونیم که مثلا CreateWeeklyReportDataSource چی هست. فقط می‌دونیم که یک لیست نهایی تهیه و به متد Pivot ارسال شده. شما در این قسمت (در حین تهیه متد CreateWeeklyReportDataSource) فرصت دارید که دیتاسورس مناسبی رو تهیه کنید.
    در مورد چک لیست هم به همین صورت. مهم تشکیل List دیتاسورس اولیه است. مابقی توسط متد Pivot مدیریت می‌شود.
    یک مثال جدید LINQPad رو اینجا اضافه کردم که در آن تعداد روزها 30 هست و ضمنا یک شرط Where هم به آن اعمال شده که مثلا کاربر روزهای 10 تا 23 رو به دلخواه انتخاب کرده (و برنامه از اول نمی‌دونه که چه بازه‌ای مد نظر هست): sample05.linq
  • #
    ‫۱۲ سال و ۱۲ ماه قبل، چهارشنبه ۲۵ آبان ۱۳۹۰، ساعت ۱۴:۱۸
    بسیار عالی بود متشکرم
  • #
    ‫۱۲ سال و ۱۲ ماه قبل، یکشنبه ۲۹ آبان ۱۳۹۰، ساعت ۱۵:۴۲
    کتابی اخیرا منتشر شده به نام Pivot Table Data Crunching, Microsoft Excel 2010 که این مفاهیم Pivot و crosstab رو مفصل در طی 380 صفحه توضیح داده.