آموزش دستور each در پاورکوئری

آموزش استفاده از each در پاور کوئری

تا آنجا که من فهمیدم each می‌تواند جایگزین ساختار استاندارد تابعی با یک ورودی شود. برای اشاره به هر یک از آیتم‌های یک مجموعه (این مجموعه می‌تواند اعضای لیست باشد یا اعضای یک ردیف یا نام ستون‌های یک Table و ...) از each بهره می‌بریم. گاهی این علامت "_" به کمکش می‌آید. در این صورت معنایش بر حسب جایی که دارید از each استفاده می‌کنید تغییر می‌کند.

چرا از each استفاده می‌کنیم؟

حداقل دو دلیل هست که each را پرکاربرد می‌کند. مهم‌ترین دلیل کاربرپسند بودن این عبارت است و دوم خواناتر کردن کدهای بکار رفته.

مثال ۱: ساخت تابع ساده

حالا فرض کنید می‌خواهیم تابعی بسازیم که عددی را از شما بگیرد و آن را با ۳ جمع کند.

(MyValue) => MyValue + 3

می‌خواهیم شکل ساده‌تر همین فرمول را بنویسیم:

(_) => _ + 3

و حتی ساده‌تر از این هم می‌شود. دیگر پرانتزها و علامت‌ها را برمی‌داریم و از each استفاده می‌کنیم:

each MyValue + 3

نتیجه هر سه این فرمول‌ها یکسان است.

مثال ۲: استفاده از each در Table.AddColumn

رابط کاربری خود پاور کوئری هم ترجیح می‌دهد each را در توابعی چون Table.AddColumn، Table.SelectRows و Table.ColumnNames و List.Transform بکار بگیرد. می‌گویید نه! این یک مثال را با هم دنبال کنید مطمئنم تا آخرش می‌آیید.

داده‌هایی شامل یک ستون متنی (بعضی کتاب‌های اروین یالوم) و سه ستون عددی (آمار فرضی فروش آنها در بین سال‌های ۲۰۱۰ تا ۲۰۱۲ در یکی از کتابفروشی‌های همدان) است. شما فرض کنید ضمن انتقالش به ادیتور پاور کوئری مرحله Changed Type را هم پشت سر گذاشته و ما قصد داریم در یک ستون جدید به هر یک از اعداد ستون ۲۰۱۲ به عنوان پیش‌بینی فروش سال بعد ۱۰ عدد اضافه کنیم.

مثال استفاده از ایچ در توابع مختلف

برای اضافه کردن یک ستون جدید در مسیر زیر هیچکس سد راهمان نخواهد بود:

Add Column…Custom Column

در پنجره‌ای که پاور کوئری بی دریغ به رویمان می‌گشاید کد ساده زیر را می‌نویسیم:

استفاده از custom column در ایچ

پس از فشردن صمیمانه کلید OK اتفاق مورد انتظاری که در شکل بالا می‌بینید اضافه شدن ۱۰ به هر یک از مقادیر ستون ۲۰۱۲ است.

Table.AddColumn(#"Changed Type", "2013", each [2012] + 10)

عبارت each را خود پاور کوئری اضافه کرد. به هر یک از اعضای ستون ۲۰۱۲ عدد ۱۰ را اضافه کرد.

مثال ۳: استفاده از each در Table.TransformColumns

شاید دوست داشته باشید در مثالی به کارگیری each را در Table.TransformColumns نیز تجربه کنید. این تابع چنانکه از نامش پیداست برای دستکاری مقادیر ستون استفاده می‌شود. مثلا به هر یک از آیتم‌های همین ستون نام کتاب‌ها عبارت "From Yalom: " را اضافه کنیم. در سربرگ Transform گروه Text Column روی Format کلیک کنید و Add Prefix را برگزینید تا یک پنجره باز شود. در درون آن عبارت From Yalom: را مانند شکل بنویسید. خوب است پیش از فشردن صمیمانه کلید OK در همین پنجره، یک Space بعد از From Yalom: بگذارید.

استفاده از table transform column در ایچ

حالا باز هم به فرمول بار می‌رویم و تابعی را که تمام و کمال خود پاور کوئری تدارک دیده با هم بررسی می‌کنیم:

Table.TransformColumns(AddedNumber, {{"Book",

 each "From Yalom: " & _ , type text}})

ورودی اول نام Table قبلی است. قسمتی که بین دو جفت آکولاد قرار دارد شامل سه بخش است. بخش اول نام ستونی که پاور کوئری قرار است پیشوندی به هر عضو آن بیافزاید و بخش سوم تعیین فرمت ستون جدید که Text است. و اما بخش دوم: ترکیب each _ اشاره می‌کند به هر عضو ستون Book و عبارت From Yalom را با & به ابتدای هر آیتم ستون می‌چسباند.

آموزش استفاده از each در لیست‌ها در پاور کوئری

آموزش استفاده از each در "لیست‌ها" در پاور کوئری

می‌خواهیم کار را با همین فایل ادامه دهیم و چون نیاز به یک لیست داریم روی یکی از ستون‌های عددی، کلیک راست می‌کنیم. حالا باید لیستی شامل درست ۲۰ آیتم قابل مشاهده باشد. من با چشم مسلح توانستم آیتم Drill down را تقریباً در انتهای این پنجره ببینم و روی آن کلیک کنم. این کار اتفاقی مبارک را برایمان رقم می‌زند و لیستی از اعداد را برایمان تدارک می‌بیند. در تصویر ببینید.

استفاده از drill down در پاورکوئری

نکته: به جای کلیک راست روی ستون کافی بود در مرحله (Step) Changed Type روی fx کلیک کنیم و جلوی عبارت "Changed Type" در فرمول بار نام ستون را آنگونه که می‌بینید تایپ کنیم تا ستون به یک لیست تبدیل شود و بقیه ستون‌ها Remove شوند.

اضافه کردن به اعضای لیست

حالا می‌خواهم به هر یک از اعضای لیست ۱۰ تا اضافه کنم. خوف نکنید، این کار را با تابع List.Transform به سرانجام می‌رسانیم. اصطلاحاً Syntax یا نحو تابع طبق نص صریح سایت مایکروسافت یعنی عبارتی که شکل نوشتن و آرگومان‌ها و خروجی تابع را نشان می‌دهد، چنین است:

List.Transform(list as list, transform as function) as list

پس آرگومان اول تابع یک لیست و آرگومان دوم آن یک تابع است و صد البته خروجی هم یک لیست. لابد می‌گویید این دیگر چه جور تابعی است. آره تا چشممان عادت کند کمی طول می‌کشد. بپذیرید در عبارت زیر آرگومان دوم واقعاً تابع است:

= List.Transform(ListOfNumbers, each _ +10)

در مورد "هستی" هم اگر به این همانی رسیدید یعنی عمیق‌تر شده‌اید. حالا اینجا می‌توانید کد زیر را جایگزین بالایی کنید. هر دو یک حرف می‌زنند:

= List.Transform(ListOfNumbers, (Item) => Item +10)

و این کد:

= List.Transform(ListOfNumbers, (_) => _ +10)

چه ترکیب each _ چه Item داخل و بیرون پرانتز و چه _ در مثال سوم همه اشاره می‌کنند به اعضای لیست ListOfNumbers.

پذیرش تابع

گفتم بپذیرید این عبارات تابع هستند. این پذیرش دو سه هفته‌ای طول می‌کشد. این دیگر کارکرد مغز است. برای من هم که برنامه‌نویس نبودم و فقط درصدی با VBA آشنا بودم و سنی ازم گذشته بود بیش از این طول کشید تا این شکل را به صورت تابع بپذیرم. در ادامه دوباره روی fx کلیک کنید تا باز هم نام مرحله قبلی را در فرمول بار ببینید (ListOfNumbers). حالا صفحه کلید را به لمس سرانگشتان خود مفتخر کنید و همان عبارت را اینگونه تغییر دهید:

= List.Transform(ListOfNumbers, each _ +10)

آرگومان دوم یا همان تابع را بخوانیم: به هر عضو لیست، ۱۰ تا اضافه کن. پس در اینجا علامت _ به هر عضو لیست اشاره می‌کند. این تابع چنانکه از نامش پیداست برای اعمال تغییر روی اعضای لیست بکار می‌رود.

مثال در VBA

شاید اگر به نتیجه توجه کنید باورپذیر بشود.

استفاده از list transform در ایچ

 این حلقه را در VBA بیاد بیاورید و فرض کنید در محیط اکسل هستید: اعداد ۱ تا ۱۰ را در ستون اول داریم و در ستون دوم به هر یک ۱۰ را می‌افزاییم:

For I = 1 To 10
    Cells(I, 2).Value = Cells(I, 1).Value + 10
Next I

مثال‌های بیشتر

به باور من هر چه بیشتر مثال ببینید برای جا افتادن مطلب بهتر است. سه کد زیر را یک به یک جایگزین کدهای قبلی کنید و قبل از جایگزینی فکر کنید نتیجه چه خواهد بود بعد حدستان را به امتحان بنشینید:

= List.Transform(ListOfNumbers, each _ *_)
= List.Transform(ListOfNumbers, (Item) => Item *Item)
= List.Transform(ListOfNumbers, each Number.Power(_ , 2))

استفاده از List.Select

حالا دست به دامان List.Select می‌شویم. ورودی‌های این تابع نیز یک لیست و یک تابع است. اعضایی از لیست را به انتخاب کاربر و بر اساس تابع در خروجی بی سر و صدا تحویل می‌دهد. ببینیم چگونه:

در همین مرحله خیلی محترمانه باز هم روی fx کلیک کنید تا یک Step جدید ساخته شود. نام Step را خود پاور کوئری می‌سازد ولی اگر برای تغییر نام Step اعمال سلیقه کنیم پاور کوئری مانعمان نمی‌شود. حالا به شکلی که در زیر می‌بینید تابع List.Select را اعمال کنید و نتیجه را در شکل ببینید:

List.Select(Add10 , each _<80)

بخوانیم آرگومان دوم را: هر عضوی از لیست Add10 را که از ۸۰ کوچکتر است در خروجی تحویل بده.

استفاده از list select در ایچ

اضافه کردن شرط

موافقید یک شرط دیگر نیز اضافه کنیم بدین صورت که علاوه بر شرط قبلی اعضایی که بزرگتر از ۲۰ هستند نیز در خروجی بیایند. معطل چی هستید؟ مرحله‌ای جدید با کلیک روی fx بنا کنید و عبارت زیر را در آن بنویسید (and را در خط فرمان ببینید، چه مشفقانه دو شرط را در آغوش یکدیگر جای می‌دهد):

اضافه کردن شرط به list select  در ایچ
List.Select(LessThan80 , each _ >20 and _<80)

با not هم خودتان به شکل زیر امتحان کنید و پاسخ را ببینید:

List.Select(LessThan80 , each not(_ >20 and _<80))

اگر موافق باشید و همراه، اعداد زوج را از لیست جدا کنم. برای این کار هر عضو را از فیلتر تابع Number.IsEven بصورتی که می‌بینید عبور می‌دهم. (در حالت عادی ورودی تابع عدد است اگر زوج باشد True و اگر فرد باشد False را برمی‌گرداند البته که اعضاء لیست ما هم عدد هستند) با دقت به مکان علامت _ توجه کنید. با وجود این علامت _ تابع هر بار یک عضو لیست TwoCondition را در خود جای می‌دهد.

فیلتر کردن اعداد زوج و فرد در ایچ

آرگومان دوم را بخوانیم: تابع Number.IsEven هر عضو TwoCondition را چک می‌کند اگر زوج بود به خروجی می‌فرستد.

نکته: دقت کرده باشید تابع Number.IsEven یک ورودی می‌گیرد پس می‌توان تابع را به صورت ساده‌تری نوشت و از ترکیب each _ نیز صرف نظر کرد:

List.Select(ListOfNumbers, Number.IsEven)

each _ در Record ها

به جدول فروش کتاب‌ها برمی‌گردیم. هدف: در هر ردیف، نشاندن مقدار ماکزیمم سه ستون عددی در یک ستون جدید است.

داده های خام و اصلی برای ایچ در پاورکوئری

حالا در تصویر بعد پنجره Custom Column که فقط یک علامت _ پس از مساوی درج شده حاصل کار هم دیده می‌شود به ردیف اول ستون جدید و گوشه پایین سمت چپ نگاه کنید خودبخود متوجه می‌شویم که در اینجا اشاره این علامت _ با رکوردها (سطرها) ی یک Table است.

استفاده از custom column در ایچ

خواننده محترم لطفا خودت دست به کیبورد شو و در این مسیر با من همراه. پس از فشردن کلید OK، در نوار فرمول تابع را به این شکل می‌بینید:

Table.AddColumn(#"Changed Type", "MaxOfRow", each _, type number)

نکته: با دست خودم ورودی آخر تابع را (type number) را اضافه کردم تا نه حالا نه هیچ وقت دیگر نیازی به تغییر فرمت ستون نباشد. حالا می‌فهمیم Table.AddColumn ممکن است دارای چهار ورودی باشد. یک Table در آرگومان اولش، نام ستون جدید و تابعی که ممکن است روی یک ستون، حتی چند ستون یا روی تک تک ردیف‌ها یا ... اعمال شود. و دست آخر تعیین فرمت ستون.

چه در همان پنجره Custom Column چه در نوار فرمول (formula bar) می‌توانید تابع را به شکلی تغییر دهید که در چشم برهم‌زدنی هر یک از رکوردها به لیست مبدل شوند. اکنون ما در هر ردیف ستون مذکور یک سطر جدول را از جنس لیست داریم و می‌توانیم با استفاده از توابع زیر مجموعه لیست، برایشان تصمیم جدید بگیریم:

= Record.ToList(_)

روی تک تک ردیف‌های ستون MaxOfRow کلیک کنید (اگر با دست خودتان تابع را نوشته باشید خوش‌آیند است)

اضافه کردن record list برای کد در ایچ

من قصد دارم بین اعضاء این لیست‌ها ماکزیمم هر لیست را در ستون نگهدارم ولی با شرایط کنونی که در لیست هم عدد هست هم رشته متنی آیا می‌توان از پاور کوئری چنین انتظاری داشت؟ واضح است که باید از تابع List.Skip استفاده کنیم و عضو اول لیست را که هم جنس بقیه نیست بپرانیم.

می‌توان یک ستون جدید ایجاد کرد و تابع را به شکل زیر در آن نوشت توجه کنید اعضاء ستون MaxOfRow، لیست هستند و می‌توانند در آرگومان اول تابع List.Skip خودنمایی کنند:

List.Skip([MaxOfRow], 1)

اما ما چنین نمی‌کنیم و مثل یک حرفه‌ای در نوار فرمول یا در همان پنجره Custom Column قبلی، تابع را به شکل زیر تغییر می‌دهیم. در شکل، ردیف سوم ستون MaxOfRow، Highlight است و خبری از "The Spinoza Problem" هم نیست:

استفاه از list skip برای کد در ایچ
Table.AddColumn(#"Changed Type", "MaxOfRow", each List.Skip(Record.ToList(_), 1))

حالا می‌توان از این لیست که اعضاءَش به عدد بودن خود باور دارند، ماکزیمم را استخراج کرد. در ادامه با بکارگیری List.Max ما هم چنین می‌کنیم در نوار فرمول عبارت بعد از each را به شکل زیر تغییر دهید:

استفاده از max row برای کد در ایچ
List.Max(List.Skip(Record.ToList(_), 1))

در ستون آخر می‌بینید که حق به حق دار رسیده و به ترتیب ۱۵ و ۳۰ و ۴۵ و ... جای خود را در سطر محکم کرده‌اند و هنوز این علامت _ به ردیف (رکورد)های جدول اشاره می‌کند.

نکته: فرض کنید در ستون‌های عددی برخی از سطرها همه مقادیرشان صفر بود و ما می‌خواهیم این سطرها را پیش از بارگذاری در اکسل، حذف کنیم. کافی بود بجای List.Max از List.Sum استفاده کنیم و با فیلتر کردن، هر چه صفر را از ستون MaxOfRow حذف کنیم (بجای نوشتن یک if تودرتو و برداشتن تمام True ها در ستون MaxOfRow).

each _ در Table ها

قصد داریم در یک مثال ساده به ابتدای نام ستون‌های یک جدول عبارت Perfix. را اضافه کنیم. همین مثال قبلی را بارگذاری می‌کنیم. پاور کوئری تغییر نام مرحله Changed Type را یک شیطنت کوچولو تلقی خواهد کرد و از آن بسادگی می‌گذرد. ما هم از نام عریض و طویل Changed Type در مرحله بعد خلاص خواهیم شد.

داده های اصلی و خام برای ایچ در اکسل

روی fx به آرامی کلیک کنید تا پاور کوئری یک Step جدید بسازد سپس داخل نوار فرمول کد زیر را بنویسید:

= Table.TransformColumnNames(ChType, each "Prefix." & _)

درست است که نام Table را در آرگومان اول خود دارد، اسم تابع می‌گوید: من با سر ستون‌ها کار دارم و قصد تغییر آنها را دارم نه با خود Table. با وجود این علامت _ شبیه یک حلقه عمل می‌کند و در هر لحظه یک سر ستون را در خودش می‌ریزد و عبارت Perfix. را به ابتدای آن می‌چسباند. به عبارتی ما به پاور کوئری می‌گوییم: به ابتدای هر سر ستون یک Perfix. بچسبان.

ساتفاده از perfix برای کد در ایچ

each در متن‌هایی مثل شرح حسابداری

یک ستون تکست داریم (ستون اول جدول قبلی با کمی دستکاری) که در ادیتور پاور کوئری Load شده است. هدف برداشتن تمام جاهای خالی از دور و بر و لابلای این رشته‌هاست. اگر خواستید از مسیر Add Column… From Text … Extract… Length طول هر رشته را بیابید. ولی قول بدهید Step را بعد از دیدن طول‌ها حذف کنید.

مرحله Changed Type را نیز اضافه کردم و تغییر نام دادم. توجه کنید نام Query را نیز به TextCleaner تغییر دادم. شما نیز چنین کنید و نام Query ها و Step ها را متناسب با کاری که می‌کنند تغییر دهید.

استفاده از text cleaner در ایچ

به سراغ یک Custom Column می‌رویم و تابع Text.Trim را روی ستون اعمال می‌کنیم. نتیجه را در شکل پایین می‌بینید:

استفاده از text trim برای کد در ایچ
= Text.Trim([Book])

تابع بالا جاهای خالی اطراف و اکناف هر رشته را حذف می‌کند. هنوز برای حیرت کردن زود است. به سراغ یک Custom Column دیگر می‌رویم. این بار تابع Text.Split را به شکل زیر صدا می‌زنیم:

استفاده از text split برای کد در ایچ
Text.Split([Custom], " ")

چنانکه انتظار می‌رفت خروجی تابع برای هر سلول در ستون آخر، یک لیست است. اگر مایلید با رفتار تابع اخیرالذکر بیشتر آشنا شوید روی تک تک سلول‌های ستون Custom.1 کلیک کنید و لیست‌های ساخته شده را از نظر بگذرانید. حالا قرار است از تابع List.Select بهره گرفته و از هر یک از لیست‌های موجود تمام جاهای خالی را حذف کنیم یا به عبارتی فقط اعضایی از هر لیست را که یک رشته متنی است نگه داریم. این تابع دارای دو آرگومان است. اولین آرگومانش یک لیست و دومین آرگومان تابعی که روی اعضای لیست اعمال می‌شود. چنانکه از نام تابع آشکار است برخی از اعضا را نگه می‌دارد و مابقی را حذف می‌کند. حاصل کار را با دقت در شکل زیر ببینید:

استفاده از list select برای فیلتر کردن لیست ها در ایچ
List.Select([Custom.1], each _ <> "")

در شکل بالا جاهای خالی بین کلمات نیز حذف شده است.

تابع Text.Combine دو آرگومان دارد که اولی لیستی از رشته‌های متنی و دومی تعیین کاراکتری است که اجزای لیست را به هم می‌چسباند. ما آرگومان دوم را این " " (یک جای خالی) انتخاب می‌کنیم. به ترتیبی که در فرمول زیر می‌بینید:

استفاده از text combine برای پیدا کردن اشتراک های لیست در ایچ
Text.Combine([Custom.2], " ")

در این مرحله سه ستون کمکی وسط را حذف می‌کنم.

خذف ستون های کمکی برای فیلتر کردن های لیست در ایچ

جمع‌بندی همه Step ها در یک فرمول تو در تو

حالا مایلم همه Step ها را بصورت یک فرمول تو در تو و در یک مرحله جمع کنم. برای اینکار پس از بارگذاری تک ستون اولیه در پاور کوئری یک Custom Column فراخوان کنید و ضمن تغییر نام آن کجا کدهای زیر را در آن بنویسید:

جمع بندی تمام step ها برای کد در ایچ
Text.Combine(
  List.Select(
    Text.Split(
       Text.Trim([Book]), " "), 
       each _ <> ""
        ), 
        " ")

اگر ضرورت ایجاب کرد از آنها یک تابع بسازید:

(TEXT) =>
let
  OUTPUT = Text.Combine(
  List.Select(
    Text.Split(
       Text.Trim(TEXT), " "), 
       each _ <> ""
        ), 
        " ")
in
OUTPUT

و هرجا لازم شد آن را فراخوان کنید. مثلا همان تک ستون اولیه را دارید، می‌توانید به شکل زیر از تابع خود ساخته استفاده کنید. در اینجا تابع، TextCleaner نام‌گذاری شده. باز هم ستونی ایجاد کرده‌ام و تابع را به شکل زیر در آن درج کردم. در دل تابع نیز به ستون Book اشاره می‌کنم:

ایجاد تابع برای step های ایجاد شده در ایچ
fnTextCleaner([Book])
📚 فایل‌های پیوست

🩹 برچسب‌ها

به عنوان اولین نفر، تجربه یا دیدگاه خود را بنویسید!

محتوای این فیلد خصوصی است و به صورت عمومی نشان داده نخواهد شد.

متن ساده

  • تگ‌های HTML مجاز نیستند.
  • خطوط و پاراگراف‌ها بطور خودکار اعمال می‌شوند.
کد امنیتی