Array

Универсальное значение для работы с массивами/матрицами на форме

Элемент <Array> позволяет объединять матрицы, фильтровать, сортировать и группировать данные, а так же изменять структуру данных и многое другое.

О всех возможностях <Array> можно узнать в статье по ссылке.

Логика

Логика элемента <Array> заключается в последовательном применении описанных операторов. Таким образом, результат выполнения предыдущей операции будет входным значением следующего оператора.

Рассмотрим пример фильтрации матрицы, сортировки значений и изменение структуры итоговой матрицы:

<Array>
  <Source>
    <DataConnection SourceDataConnection="CityPrimaryGetDataConnection">
      <Fields>
        <Field Name="CityId" />
        <Field Name="Title" />
        <Field Name="Archive" />
      </Fields>
    </DataConnection>
  </Source>
  <Filter>
    <Index Value="2" />
    <Value>False</Value>
  </Filter>
  <Order>
    <By Index="1" />
  </Order>
  <Select>
    <Items>
      <Item Type="Number" Start="1"/>
      <Item Type="Index">0</Item>
      <Item Type="Index">1</Item>
    </Items>
  </Select>
</Array>

Исходная матрица (массив) всегда задается в тэге <Source>. В нем можно указывать загружающее соединение с данными любого типа, результат выполнения команды либо значения любого объекта на форме.

В примере передаем PrimaryGetDataConnection со значениями:

+---+--------------+-------+
| 1 | Екатеринбург | False |
| 2 | Челябинск    | False |
| 3 | Москва       | False |
| 6 | Уфа          | True  |
+---+--------------+-------+

Первым выполнится оператор <Filter> с типом Equal (по умолчанию), который отфильтрует строки матрицы по значению False в колонке Archive. Данные после фильтрации:

+---+--------------+-------+
| 1 | Екатеринбург | False |
| 2 | Челябинск    | False |
| 3 | Москва       | False |
+---+--------------+-------+

Затем оператор <Order> отсортирует значения по колонке Title по возрастанию (по умолчанию).

+---+--------------+-------+
| 1 | Екатеринбург | False |
| 3 | Москва       | False |
| 2 | Челябинск    | False |
+---+--------------+-------+

Последним оператором идет <Select>, который задает набор селекторов для формирования структуры результирующей матрицы. Первой колонкой идет селектор Number, который задает порядковый номер строки. Для второй и третьей колонок используется самый простой и распространенный вариант селекторов - Index - индекс колонки входной матрицы. Порядок описания селекторов задает порядок столбцов результирующей матрицы.

+---+---+--------------+
| 1 | 1 | Екатеринбург |
| 2 | 3 | Москва       |
| 3 | 2 | Челябинск    |
+---+---+--------------+

На последовательном выполнении различных операторов строится работа с элементом <Array>. Главное соблюдать соответствие разрядности результата одной операции и ожидаемой разрядности входных данных следующего оператора.


Дальше рассмотрим несколько задач, в которых познакомимся с другими операторами и возможностью их комбинировать. Работать будем с формой и запросами из раздела Подготовка.

Входные значения

В предыдущем примере рассмотрели вариант, когда в качестве источника данных для <Array> используется загружающий DataConnection. Матрицу можно получить и из таблицы DatabaseTable через get-проперти ArrayData. Но для получения данных из таблицы можно использовать get-проперти DictionaryArrayData, чтобы получить массив словарей.

<Array>
  <Source>
    <Object Name="ClientDatabaseTable">
      <Property Name="DictionaryArrayData" />
    </Object>
  </Source>
  <Select>
    <Items>
      <Item Type="Field">ClientId</Item>
      <Item Type="Field">Title</Item>
      <Item Type="Field">Archive</Item>
    </Items>
  </Select>
  <Filter>
    <Index Value="2" />
    <Value>False</Value>
  </Filter>
  <Order>
    <By Index="1" />
  </Order>
  <Select>
    <Items>
      <Item Type="Number" Start="1"/>
      <Item Type="Index">0</Item>
      <Item Type="Index">1</Item>
    </Items>
  </Select>
</Array>

При работе со словарями используется селектор Field, который по имени колонки вернет значения всех ее ячеек. Так будет удобнее извлекать нужные колонки таблицы и не нужно следить за индексами колонок, которые могут сдвинутся, если добавится или удалится непоследняя колонка.

После получения нужных колонок, можно применять различные операторы для получения нужных значений. Например, фильтровать матрицу, упорядочивать значения и т.д.

Значение <Array> в текст

Чтобы значение тэга <Array> преобразовать в строку и отобразить в текстовом поле, следует использовать оператор <StringJoin>.

<Array>
  <Source>
    <DataConnection SourceDataConnection="OrderPositionPrimaryGetDataConnection">
      <Fields>
        <Field Name="ClientId" />
        <Field Name="MaterialCategoryTitle" />
        <Field Name="MaterialTitle" />
        <Field Name="UnitShortTitle" />
        <Field Name="Quantity" />
        <Field Name="UnitPrice" />
      </Fields>
    </DataConnection>
  </Source>
  <StringJoin Separator="\t" LineSeparator="\r\n" />
</Array>

Оператор задает разделители между элементами одной строки матрицы (атрибут Separator) и между строками матрицы (атрибут LineSeparator). Для линейных массивов будет использоваться только разделитель из атрибута Separator.

Кстати, оператор StringJoin можно использовать для проверки значений DataConnection или массива, отображение которых не предусмотрено на форме, но необходимо проверить корректность промежуточных расчетов.

Для этого нужно в тэг <Hint> любого доступного графического элемент добавить <Array> с оператором StringJoin.

На форме есть ResultTextBox, его будем использовать для отображения результата применения операторов элемента <Array>. Давайте выведем в текстовое поле содержимое OrderPositionPrimaryGetDataConnection, для этого в тэг <Text> объекта ResultTextBox вставим код приведенный выше.

Откроем форму в приложении и проверим результат:

Join двух матриц

Один из самых простых примеров использования массивов является соединение двух матриц. У нас на форме есть список клиентов с идентификаторами городов и список самих городов. Давайте соединим оба списка с помощью оператора <Join>, так чтобы в новом списке для каждого клиента было название его города.

<Array>
  <Source>
    <DataConnection SourceDataConnection="ClientPrimaryGetDataConnection">
      <Fields>
        <Field Name="ClientId" />
        <Field Name="Title" />
        <Field Name="CityId" />
      </Fields>
    </DataConnection>
  </Source>
  <Join>
    <OuterArray>
      <DataConnection SourceDataConnection="CityPrimaryGetDataConnection">
        <Fields>
          <Field Name="CityId" />
          <Field Name="Title" />
        </Fields>
      </DataConnection>
    </OuterArray>
    <Keys>
      <Inner Index="2" />
      <Outer Index="0" />
    </Keys>
    <Result>
      <Inner Index="0" /><!-- ClientId -->
      <Inner Index="1" /><!-- ClientTitle -->
      <Outer Index="0" /><!-- CityId -->
      <Outer Index="1" /><!-- CityTitle -->
    </Result>
  </Join>
</Array>

В тэге <OuterArray> указываем внешнюю матрицу, которая будет соединяться с исходной матрицей по ключам, заданным в тэге <Keys>.

Количество <Inner> и <Outer> индексов в тэге <Keys>должно совпадать.

Если ключей (пар индексов) больше одного, то важен порядок их описания, так как <Inner> и <Outer> индексы сравниваются по парно в порядке описания.

В тэге <Result> задаем структуру результирующей матрицы, указывая индексы колонок внутренней и внешней матрицы и их порядок.

Скопируйте код выше и замените значение тэга <Text> у объекта ResultTextBox. Не забудьте добавить оператор <StringJoin>.

Чтобы результат был понятнее, добавим пробелы в строки для выравнивания колонок.

Подстановка значений

Задачу из предыдущего примера можно решить с помощью селектора Substitution, который по ключу выбирает значение из таблицы подстановки и добавляет его в строку результирующей матрицы.

<Array>
  <Source>
    <DataConnection SourceDataConnection="ClientPrimaryGetDataConnection">
      <Fields>
        <Field Name="ClientId" />
        <Field Name="Title" />
        <Field Name="CityId" />
      </Fields>
    </DataConnection>
  </Source>
  <Select>
    <Items>
      <Item Type="Index">0</Item>
      <Item Type="Index">1</Item>
      <Item Type="Index">2</Item>
      <Item Type="Substitution">
        <Index>2</Index>
        <Value>
          <DataConnection SourceDataConnection="CityPrimaryGetDataConnection">
            <Fields>
              <Field Name="CityId" />
              <Field Name="Title" />
            </Fields>
          </DataConnection>
        </Value>
      </Item>
    </Items>
  </Select>
</Array>

В тэге <Index> селектора Substitution указываем колонку исходной матрицы, по значениям которой будет происходить выборка значений из таблицы подстановки.

В качестве таблицы подстановки должна быть матрица, состоящая из двух столбцов. В первом столбце должен быть ключ, по которому будет идти подстановка, а во втором - значение, которое будет использоваться при подстановке. В нашем случае это CityPrimaryGetDataConnection.

Выражение

Селектор Expression вычисляет выражение на основе элементов исходного массива (указываются в квадратных скобках []) и/или дополнительных значений (указываются в фигурных скобках {}), перечисленные в тэге <Items> вложенном в <Item Type="Expression">.

Давайте с помощью селектора Expression подсчитаем стоимость для каждой позиции заказа:

<Array>
  <Source>
    <DataConnection SourceDataConnection="OrderPositionPrimaryGetDataConnection">
      <Fields>
        <Field Name="ClientId" />
        <Field Name="MaterialTitle" />
        <Field Name="UnitShortTitle" />
        <Field Name="Quantity" />
        <Field Name="UnitPrice" />
      </Fields>
    </DataConnection>
  </Source>
  <Select>
    <Items>
      <Item Type="Index">0</Item>
      <Item Type="Index">1</Item>
      <Item Type="Index">2</Item>
      <Item Type="Index">3</Item>
      <Item Type="Index">4</Item>
      <Item Type="Expression">
        <Expression>[3] * [4]</Expression>
      </Item>
    </Items>
  </Select>
</Array>

Проверим результат на форме:

Group By Id

В этом примере рассмотрим, как подсчитать стоимость всех товаров для каждого клиента. Для этого будем использовать операторы <Group> и <Aggregate>, а так же селектор Action.

<Array>
  <Source>
    <DataConnection SourceDataConnection="OrderPositionPrimaryGetDataConnection">
      <Fields>
        <Field Name="ClientId" />
        <Field Name="MaterialTitle" />
        <Field Name="Quantity" />
        <Field Name="UnitPrice" />
      </Fields>
    </DataConnection>
  </Source>
  <Group>
    <By Index="0" />
  </Group>
  <Select>
    <Items>
      <!-- элемент группировки -->
      <Item Type="Index">0</Item>
      <Item Type="Action">
        <Index>1</Index>
        <!-- работаем с вложенным массивом -->
        <Actions>
          <Aggregate>
            <Items>
              <Item>
                <Result>0</Result>
                <Expression>{Result} + [2] * [3]</Expression>
              </Item>
            </Items>
          </Aggregate>
        </Actions>
      </Item>
    </Items>
  </Select>
</Array>

Результатом выполнения оператора <Group> будет матрица вида:

+---+-------------------------------------+
| 2 | +---+------------------+----+-----+ |
|   | |	2 | SvetoCopy пачка  | 20 | 350 | |
|   | |	2 | Ручка шариковая  | 15 | 25  | |
|   | +---+------------------+----+-----+ |
+---+-------------------------------------+
| 4 | +---+------------------+----+-----+ |
|   | |	4 | Карандаш простой | 10 | 7   | |
|   | |	4 | Линейка 30 см    | 10 | 20  | |
|   | +---+------------------+----+-----+ |
+---+-------------------------------------+
| 9 | +---+------------------+----+-----+ |
|   | |	9 | SvetoCopy пачка  | 10 | 350 | |
|   | | 9 | Ручка шариковая  | 60 | 25  | |
|   | +---+------------------+----+-----+ |
+---+-------------------------------------+

Так как в тэге <By> в качестве условия группировки указали индекс колонки ClientId, то первая колонка результата содержит уникальные значения идентификаторов клиентов. Вторая колонка - вложенные таблицы с записями, соответствующими идентификатору группы. Вложенные таблицы имеют ту же структуру, что и входная матрица.

Чтобы подсчитать сумму значений всех строк матрицы используется оператор <Aggregate>, который для каждой строки вычисляет выражение из тэга <Expression> и суммирует полученное значение с суммой значений выражения для предыдущих строк.

<Aggregate>
  <Items>
    <Item>
      <Result>0</Result>
      <Expression>{Result} + [2] * [3]</Expression>
    </Item>
  </Items>
</Aggregate>

В выражении <Expression> в квадратных скобках указаны индексы элементов исходного массива - Quantity и UnitPrice. Переменная {Result}, используется для хранения значения, вычисленного для предыдущей строки. В тэге <Result> задали начальное значение для переменной результата.

Когда оператор завершит вычисление, то вернет получившееся значение переменной {Result}. Так как в нашем примере только один элемент <Item> в тэге <Items>, то вернется скалярное значение. Если бы их было несколько, то вернулся бы массив результатов агрегации.

Так как матрицы, для которых нужно подсчитать сумму, являются вложенными, то используем селектор Action, чтобы к матрице в каждой строке применить оператор <Aggregate> .

<Select>
  <Items>
    <!-- элемент группировки -->
    <Item Type="Index">0</Item>
    <Item Type="Action">
      <Index>1</Index>
      <!-- работаем с вложенным массивом -->
      <Actions>
        <Aggregate>
          <Items>
            <Item>
              <Result>0</Result>
              <Expression>{Result} + [2] * [3]</Expression>
            </Item>
          </Items>
        </Aggregate>
      </Actions>
    </Item>
  </Items>
</Select>

В тэге <Index> указали индекс колонки с вложенными массивами.

В тэге <Actions> указали операцию над массивами. Этот тэг аналогичен тэгу <Array> - мы можем в нем использовать любые операторы и их комбинации. Эти операции будут применяться к каждой строке отдельно. Исходным массивом для первой операции будет элемент массива с индексом из тэга <Index>.

Таким образом, тэгом <Select> мы задали структуру результирующей матрицы, которая будет хранить значения:

+-----+--------+
|  2  |  7375  |
|  4  |  270   |
|  9  |  5000  |
+-----+--------+

Добавьте в массив оператор <Join>, чтобы в ответе были фамилии клиентов. Откройте форму и проверьте ответ:

Last updated