<?xml version='1.0' encoding="utf-8"?>
      <rss version='2.0'>
      <channel>
      <title>Форум на Исходниках.RU</title>
      <link>https://forum.sources.ru</link>
      <description>Форум на Исходниках.RU</description>
      <generator>Форум на Исходниках.RU</generator>
  	
      <item>
        <guid isPermaLink='true'>https://forum.sources.ru/index.php?showtopic=375199&amp;view=findpost&amp;p=3299278</guid>
        <pubDate>Wed, 10 Apr 2013 08:08:00 +0000</pubDate>
        <title>Перебор записей в TDataSet</title>
        <link>https://forum.sources.ru/index.php?showtopic=375199&amp;view=findpost&amp;p=3299278</link>
        <description><![CDATA[Fr0sT: <strong class='tag-b'><span class="tag-color tag-color-named" data-value="purple" style="color: purple"><span class='tag-size' data-value='14' style='font-size:14pt;'>Перебор записей в TDataSet</span></span></strong><br>
<br>
Иногда при переборе записей из датасета хочется написать простой пропуск итерации:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">&nbsp;&nbsp;...</div><div class="code_line">&nbsp;&nbsp;if DS.FieldByName(&#39;foo&#39;).IsNull then Continue;</div><div class="code_line">&nbsp;&nbsp;...</div></ol></div></div></div></div><script>preloadCodeButtons('1');</script><br>
- ан нет. Необходимость строчки DS.Next держит нас в рамках цикла словно пудовая гиря. Приходится вставлять ветвления, громадные вложенные begin-end и прочее. Я уже не говорю о случае, когда DS.Next просто забываешь поставить.<br>
Предлагаю пару простеньких оберток, добавляющих синтаксического сахара к перебору записей.<br>
<br>
<strong class='tag-b'>Способ 1.</strong> for-in для датасета (добавляем итератор). Для Дельфи начиная с XE (используются хелперы).<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">{$IF CompilerVersion &#62;= 22} // RAD_XE+</div><div class="code_line">// реализация энумератора для TDataSet</div><div class="code_line">type</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;// заглушка для подставки в условие цикла. Реально нигде не используется</div><div class="code_line">&nbsp;&nbsp;TDataSetLoop = record</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;TDataSetEnum = class</div><div class="code_line">&nbsp;&nbsp;private</div><div class="code_line">&nbsp;&nbsp; &nbsp;FOwner: TDataSet;</div><div class="code_line">&nbsp;&nbsp; &nbsp;FListBegin: Boolean; &nbsp;// чтобы не пропустить первую запись</div><div class="code_line">&nbsp;&nbsp; &nbsp;function GetCurrent: TDataSetLoop;</div><div class="code_line">&nbsp;&nbsp;public</div><div class="code_line">&nbsp;&nbsp; &nbsp;constructor Create(aOwner: TDataSet);</div><div class="code_line">&nbsp;&nbsp; &nbsp;function MoveNext: Boolean;</div><div class="code_line">&nbsp;&nbsp; &nbsp;property Current: &nbsp;TDataSetLoop read GetCurrent;</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;TDataSetEx = class helper for TDataSet</div><div class="code_line">&nbsp;&nbsp; &nbsp;// возвращает объект-enumerator для конструкции for..in</div><div class="code_line">&nbsp;&nbsp; &nbsp;function GetEnumerator: TDataSetEnum;</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">{ TDataSetEnum }</div><div class="code_line">&nbsp;</div><div class="code_line">constructor TDataSetEnum.Create(aOwner: TDataSet);</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;inherited Create;</div><div class="code_line">&nbsp;&nbsp;FOwner := aOwner;</div><div class="code_line">&nbsp;&nbsp;FListBegin := True;</div><div class="code_line">&nbsp;&nbsp;FOwner.First;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">function TDataSetEnum.GetCurrent: TDataSetLoop;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;// ничего не делать</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">{ Принципы прохода сильно различаются у for-in и по датасету:</div><div class="code_line">&nbsp;&nbsp;у for-in - while MoveNext do ..., у датасета - do ...; MoveNext; while not Eof</div><div class="code_line">&nbsp;&nbsp;(причем Eof устанавливается только после MoveNext с последней записи). Поэтому</div><div class="code_line">&nbsp;&nbsp;для согласования начала цикла вводится флаг FListBegin (для первой итерации не</div><div class="code_line">&nbsp;&nbsp;двигать указатель записи) и дополнительная проверка на Eof после перехода на</div><div class="code_line">&nbsp;&nbsp;следующую запись }</div><div class="code_line">function TDataSetEnum.MoveNext: Boolean;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := not FOwner.Eof;</div><div class="code_line">&nbsp;&nbsp;if Result then</div><div class="code_line">&nbsp;&nbsp; &nbsp;if not FListBegin then</div><div class="code_line">&nbsp;&nbsp; &nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;FOwner.Next;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;if FOwner.Eof then Result := False;</div><div class="code_line">&nbsp;&nbsp; &nbsp;end</div><div class="code_line">&nbsp;&nbsp; &nbsp;else</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;FListBegin := False;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">{ TDataSetEx }</div><div class="code_line">&nbsp;</div><div class="code_line">function TDataSetEx.GetEnumerator: TDataSetEnum;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := TDataSetEnum.Create(Self);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">{$IFEND}</div></ol></div></div></div></div><br>
<br>
Пример использования:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;loop: TDataSetLoop;</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;RecCount := 0;</div><div class="code_line">&nbsp;&nbsp;for loop in qCurr do</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;if qCurr.FieldByName(&#39;notnull&#39;).IsNull then Break;</div><div class="code_line">&nbsp;&nbsp; &nbsp;if qCurr.FieldByName(&#39;foo&#39;).IsNull then Continue;</div><div class="code_line">&nbsp;&nbsp; &nbsp;if qCurr.FieldByName(&#39;bar&#39;).IsNull then Continue;</div><div class="code_line">&nbsp;&nbsp; &nbsp;...</div><div class="code_line">&nbsp;&nbsp; &nbsp;Inc(RecCount);</div><div class="code_line">&nbsp;&nbsp;end;</div></ol></div></div></div></div><br>
<br>
<strong class='tag-b'>+</strong> Привычный вид цикла<br>
<strong class='tag-b'>+</strong> Удобно для больших циклов<br>
<strong class='tag-b'>-</strong> Надо объявлять переменную <br>
<br>
<strong class='tag-b'>Способ 2.</strong> ForEach для датасета. Для Дельфи начиная с 2006 (используются лямбда-функции). Можно также реализовать в виде хелпера.<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">{$IF CompilerVersion &#62;= 18} // 2006+</div><div class="code_line">type</div><div class="code_line">&nbsp;&nbsp;TDataSetForEachProc = reference to procedure (DS: TDataSet; var Continue: Boolean);</div><div class="code_line">&nbsp;</div><div class="code_line">procedure DataSetForEach(DS: TDataSet; ForEachProc: TDataSetForEachProc);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;Continue: Boolean;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Continue := True;</div><div class="code_line">&nbsp;&nbsp;DS.First;</div><div class="code_line">&nbsp;&nbsp;while not DS.Eof do</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;ForEachProc(DS, Continue);</div><div class="code_line">&nbsp;&nbsp; &nbsp;if not Continue then Break;</div><div class="code_line">&nbsp;&nbsp; &nbsp;DS.Next;</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">end;</div><div class="code_line">{$IFEND}</div></ol></div></div></div></div><br>
<br>
Пример использования:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">&nbsp;&nbsp;RecCount := 0;</div><div class="code_line">&nbsp;&nbsp;DataSetForEach(qCurr, procedure(DS: TDataSet; var Continue: Boolean)</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if qCurr.FieldByName(&#39;notnull&#39;).IsNull then</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Continue := False;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Exit;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if DS.FieldByName(&#39;foo&#39;).IsNull then Exit;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if DS.FieldByName(&#39;bar&#39;).IsNull then Exit;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;...</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Inc(RecCount);</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end);</div></ol></div></div></div></div><br>
<br>
<strong class='tag-b'>+</strong> Никаких дополнительных переменных<br>
<strong class='tag-b'>-</strong> Непривычно для Дельфийского взгляда<br>
<strong class='tag-b'>-</strong> Не очень удобно для больших циклов<br>
<br>
<br>
<strong class='tag-b'>upd</strong>. Исправлен баг в for-in реализации: последняя запись обрабатывалась дважды]]></description>
        <author>Fr0sT</author>
        <category>Все языки: Статьи, заготовки в FAQ</category>
      </item>
	
      </channel>
      </rss>
	