Тема: Версия 3.0.82 Метод Content.Move

Озадачился сортировкой объектов в состве (другого объекта)
Столкнулся с проблемой: не получается изменить порядок следования
В итоге для проверки написал такую команду:

ThisObject.Content.Move ThisObject.Content.Item(4), 1

Выполнение команды ничего не дает. Объект с номером 4 в коллекции есть, но он не перемещается

Re: Версия 3.0.82 Метод Content.Move

Необходимо запрашивать и обновлять коллекцию!

set Objects = thisobject.Objects 
Objects.swap thisobject.objects(0), thisobject.objects(1) 
Objects.Update

Re: Версия 3.0.82 Метод Content.Move

Anatoly пишет:

Необходимо запрашивать и обновлять коллекцию!

Обновлять надо каждый раз после перестановки или один раз после всех перестановок?

P.S. Правильно писал Slava.Coder.Vrn, все из-за отсутствия документации

Re: Версия 3.0.82 Метод Content.Move

Написал другую команду

ThisObject.Content.Swap ThisObject.Content(0), ThisObject.Content(1)
ThisObject.Content.Update

Все равно ничего не происходит

(изменено: Anatoly, 11 мая 2007г. 09:38:41)

Re: Версия 3.0.82 Метод Content.Move

SafiullinMF пишет:

Написал другую команду

ThisObject.Content.Swap ThisObject.Content(0), ThisObject.Content(1)
ThisObject.Content.Update

Все равно ничего не происходит

Я же написал:

Необходимо запрашивать и обновлять коллекцию!

Т.е. имееся в виду, считал коллекцию, отсортировал, как надо, и обновил сортировку в базе!

Set Objects = ThisObject.Content ' - ВОТ ТУТ МЫ ЗАПРАШИВАЕМ
Objects.Swap ThisObject.Content(0), ThisObject.Content(1)
Objects.Update   ' Вот тут мы обновляем в базу

Re: Версия 3.0.82 Метод Content.Move

Ура, сработало

Re: Версия 3.0.82 Метод Content.Move

SafiullinMF пишет:

Написал другую команду

ThisObject.Content.Swap ThisObject.Content(0), ThisObject.Content(1)
ThisObject.Content.Update

Все равно ничего не происходит

Расскажу, почему так происходит:
1-я строку можно разделить на 2-е:
ThisObject.Content - Вы запросили коллекцию из базы.
.Swap obj1, obj2 - Вы поменяли местами объекты 1 и 2.
Осталось только проапдейтить  :)

2-я строка: также делим на 2-е:
ThisObject.Content - Вы запросили коллекцию из базы. (неотсортированную)  ;)
.Update - Обновить неотсортированную коллекцию.

Надеюсь, что понятно объяснил ошибку  :)

Поэтому надо сначала запрашивать, а потом апдейтить  :)

(изменено: Shoorup, 27 августа 2014г. 13:12:33)

Re: Версия 3.0.82 Метод Content.Move

Может есть у кого готовая сортировка объектов по маске?

ThisObject.Content.Sort ThisObject.Content


При такой сортировке все проходит красиво, но например если наименование объектов идет так:
01 - абвгд
02 - абвгд
11- абвгд
100 - абвгд
то отсортирует как:
01- абвгд
100-абвгд
11-абвгд
02 - абвгд
или например:

1247/2
124711
1247/100
отсортирует
1247/1
1247/100
124711
1247/2

Прошу помочь в решении задачи

Вроде решил но через опу конечно же. Сделал массив из чисел по которым должна идти сортировка. Там где знаков в конце меньше трех дописал нули впереди.
Ну а дальше во время сортировки массива попутно делаю перестановку и объектов под темиже итеративными номерами.
Единственное сортировка вставками у меня кривая. Первый элемент массива считается отсортированным. Не могу догнать как сделать так чтобы сортировался и первый элемент


For i=1 To ThisObject.Content.Count-1
  newElement = ArrObjNoNSI(i)
  Set newObjElement = Thisobject.Objects(i)
  location=i-1
  Do While (location >= 1) and (ArrObjNoNSI(location) > newElement)
     ArrObjNoNSI(location+1) = ArrObjNoNSI(location) 'тут перестановка
     Coll.Swap Thisobject.Objects(location+1), Thisobject.Objects(location) 'переставляем объекты
     Coll.Update
     location=location-1
  Loop
  ArrObjNoNSI(location+1)=newElement 'тут перестановка
  Coll.Swap Thisobject.Objects(location+1), newObjElement
  Coll.Update
Next

ArrObjNoNSI - это массив изначально неотсортированых номеров взятых у объектов которые нужно сортировать.

При сортировке 5-10 объектов -все проходит довольно быстро. Если сортировать уже 150 объектов то TDMS вешается на полчаса

(изменено: McZag, 28 августа 2014г. 11:22:38)

Re: Версия 3.0.82 Метод Content.Move

Shoorup пишет:

При сортировке 5-10 объектов -все проходит довольно быстро. Если сортировать уже 150 объектов то TDMS вешается на полчаса

Если непременно хотите сортировать сами, то сделайте на милость две вещи:
1. Не сортируйте объекты. Сортируйте индексы. Затем расставьте в один проход объекты по индексам. Это качественно сократит время. И мне кажется, что Coll.Update во внутреннем цикле лишний.
2. Используйте какой-нибудь готовый быстрый алгоритм сортировки. Погуглите, их много. Возможно для ваших данных есть какая-то специфика. Для старта: https://ru.wikipedia.org/wiki/%D0%90%D0%BB%...%B2%D0%BA%D0%B8

Я так же рекомендую попробовать динамическую сортировку. Возможно вам это будет удобнее:
1. Создайте свой алгоритм формирования строки, сортируемой по вашим правилам (например, 1247/1 -> 1247/0001, 12/100->0012/0100 и так далее).
2. Сохраняйте эту строку в атрибуте объекта каждый раз при внесении в него изменений. Показывать пользователю эту строку не обязательно.
3. Для объектов-родителей вы можете сохранить порядок сортировки дочерних объектов при их отображении: Типы объектов/Состав/Задать параметры вывода столбцов. В диалоге Редактор столбцов добавьте атрибут объекта, по которому вы будете сортировать. Снимите с него флажок "Отображать". Задайте порядок сортировки
4. Кроме этого вы можете использовать сортировку по этому столбцу в выборках.

Re: Версия 3.0.82 Метод Content.Move

McZag пишет:

Сортируйте индексы. Затем расставьте в один проход объекты по индексам.

Подскажите пожалуйста как.
Сортировка вставками в моем случае как мне кажется оптимальнее. Список как правило уже отсортирован и в конец добавляется что-то новое что нужно вставить в нужное место.
На самом деле очень не хватает команды в контекстном меню "вставить между..."  или просто каким либо образом руками передвинуть на нужное место объект. Мне не надо каждый раз кучу объектов намешаных сортировать...

Re: Версия 3.0.82 Метод Content.Move

Shoorup пишет:

Подскажите пожалуйста как.
Сортировка вставками в моем случае как мне кажется оптимальнее. Список как правило уже отсортирован и в конец добавляется что-то новое что нужно вставить в нужное место.
На самом деле очень не хватает команды в контекстном меню "вставить между..."  или просто каким либо образом руками передвинуть на нужное место объект. Мне не надо каждый раз кучу объектов намешаных сортировать...

Написал для вас небольшой код. Особо не тестировал, но по первым признакам вроде работает.

' Вставка объекта в нужное место в составе

' Код для тестирования вставки
' Преположим, что новый объект для вставки это наш текущий объект и его родитель известен
Call InsertObject(ThisObject.Parent, ThisObject)

' Процедура вставки объекта в нужное место в составе
' Предполагается, что коллекция объектов, входящих в parent исходно сортирована
Sub InsertObject(parent, object)

  Dim content, lowIndex, highIndex, testIndex, strToCompare

  ' IDDQD
  ThisScript.SysAdminModeOn

  ' А был ли мальчик?
  If object Is Nothing Then Exit Sub
  If parent Is Nothing Then Exit Sub

  ' Получаем состав объекта-родителя
  Set content  = parent.Content
  ' Получаем максимальный индекс объекта в коллекции
  highIndex = content.Count - 1
  ' Инициализируем первый индекс объекта в коллекции
  lowIndex = 0
  ' Инициализируем строку сортируемого объекта. Для примера взято описание объекта,
  ' может быть атрибут или системное свойство
  strToCompare = object.Description

  ' Алгоритм поиска места для вставки
  While lowIndex < highIndex
    ' Используем бисекцию
    testIndex = (lowIndex + highIndex) \ 2

    ' Сравниваем строки
    If StrComp(content.Item(testIndex).Description, strToCompare, vbTextCompare) = -1 Then
      lowIndex = testIndex + 1
    Else
      highIndex = testIndex
    End If

  Wend

  ' Вставляем объект в нужное место
  content.Move object, lowIndex

  ThisScript.SysAdminModeOff

End Sub

Re: Версия 3.0.82 Метод Content.Move

McZag пишет:

Написал для вас небольшой код. Особо не тестировал, но по первым признакам вроде работает.

Если состав будет очень большим, ну скажем 1000 объектов и больше, то лучше предварительно одним запросом получить через выборку весь этот состав и обязательно то значение (или набор значений), по которым вы будете сортировать. Если состав у вас уже торчит (например, вы его получаете в событии ListBeforeShow), то выборка не нужна.

Дальше работаете с массивом Sheet - он статический и обрабатывать вы его будете в памяти. Это пригодится, если вы будете извлекать условия сортировки из атрибутов: при каждом обращении типа
content.item(i).Attributes("ID") будет идти запрос в базу. А Sheet.CellValue (i, j) - уже будет в памяти.

(изменено: Shoorup, 8 октября 2014г. 10:54:05)

Re: Версия 3.0.82 Метод Content.Move

Вот только добрался до сортировки. И только сейчас до меня дошло что:

лучше предварительно одним запросом получить через выборку весь этот состав и обязательно то значение (или набор значений), по которым вы будете сортировать

и главное дошло почему.
Например у меня в составе объекта есть 300 объектов. Есть среди них различие в одном атрибуте. Значение его может быть 1245, 1246, 1247. Захотел я получить количество этих самых объектов. Т.е. сколько у меня 1245, сколько 1246 и сколько 1247. For - Next по всем объектам. внутри Case который инкрементирует счетчик количества каждого объекта. Все вроде хорошо только скорость такой простой задачи заметно маленькая - минуты 2. Ну и теперь понятно почему:) И это даже не 1000, а 300 объектов. Так что надо теперь разобраться как в скрипте сделать счетчик на основе выборки:)

$matches[1]
Все нашел: TDMSApplication.CreateQuery   :rolleyes:
Получилось! Клаааассс!  I  :wub: TDMS

Re: Версия 3.0.82 Метод Content.Move

Не могу только догнать почему мне возращает 0 столбцов

Set query1245 = ThisApplication.CreateQuery
      query1245.AddCondition tdmQueryConditionObjectDef, "OBJECT_NSI_UKAZANIE_GTSS"
      query1245.AddCondition tdmQueryConditionAttribute, "1245", "ATTR_NSI_RAZDEL_UKAZANIA"
MsgBox query1247.Sheet.ColumnsCount 'Вернет 0. почему ?
MsgBox query1247.Sheet.RowsCount      'вернет правильно 272

Re: Версия 3.0.82 Метод Content.Move

А как сортировать по индексам? Я так понимаю идея в том чтобы расставить индексы все объектам а потом что-то типа обновить?

Re: Версия 3.0.82 Метод Content.Move

по умолчанию CreateQuery не имеет столбцов (описание, статус и дата - это дефолтные столбцы) вам нужно в addcondition добавлять "+tdmQueryConditionVisible" для столбцов которые должны отображаться в результате запроса (Sheet)

Re: Версия 3.0.82 Метод Content.Move

Shoorup пишет:

Не могу только догнать почему мне возращает 0 столбцов

Set query1245 = ThisApplication.CreateQuery
      query1245.AddCondition tdmQueryConditionObjectDef, "OBJECT_NSI_UKAZANIE_GTSS"
      query1245.AddCondition tdmQueryConditionAttribute, "1245", "ATTR_NSI_RAZDEL_UKAZANIA"
MsgBox query1247.Sheet.ColumnsCount 'Вернет 0. почему ?
MsgBox query1247.Sheet.RowsCount      'вернет правильно 272

Это уже обсуждалось. Такая необычная фича TDMS. Для стандартного набора столбцов их число, возвращаемое функцией count равно 0. Надо переделывать, но есть опасение что это вызовет какие-нибудь проблемы в коде, которые это как-то используют. Добавьте столбец и их окажется ровно столько, сколько и должно быть

(изменено: McZag, 9 октября 2014г. 15:25:17)

Re: Версия 3.0.82 Метод Content.Move

Shoorup пишет:

А как сортировать по индексам? Я так понимаю идея в том чтобы расставить индексы все объектам а потом что-то типа обновить?

Я имел ввиду, что на больших объемах данных исходно не сортированные объекты лучше всего сортировать не как  объекты в составе, а отображения этих объектов на массив. Пересортировав массив любым из подходящих способов, затем методом move в один проход его расставите в составе.

У описанного подхода есть явный минус - копирование в массив и обратно. Поэтому так имеет смысл сортировать именно неупорядоченные данные большого объема. Но если ваши составы небольшие - несколько сотен объектов - это лишний код. Сортируйте сразу объекты в составе, но Update применяйте только один раз,  используя объектные переменные для обращения к коллекциям типа Thisobject.Objects. Set objs = Thisobject.Objects и вперед. TDMS не загружает сущности без явного к ним обращения и каждый раз обращаясь к Thisobject.Objects(index) вы обращаетесь в базу данных за конкретным объектом, а не к объекту коллекции. При операции инициализации коллекции через переменную, вызовется конструктор и она будет создана полностью.

Re: Версия 3.0.82 Метод Content.Move

McZag пишет:

Это уже обсуждалось. Такая необычная фича TDMS. Для стандартного набора столбцов их число, возвращаемое функцией count равно 0. Надо переделывать, но есть опасение что это вызовет какие-нибудь проблемы в коде, которые это как-то используют. Добавьте столбец и их окажется ровно столько, сколько и должно быть

Зачем переделывать? Я уже не раз проверял, tdmQueryConditionVisible работает на отлично! Только удобней кодами пользоваться (смотрите в справке) что бы код слишком не расползался

Re: Версия 3.0.82 Метод Content.Move

+tdmQueryConditionVisible помогло
Но чета я с утра видимо туплю...

Получил я допустим из временной выборки значение атрибута которое мне надо (порядок кстати по мере создания объекта). Закинул значения в массив и отсортировал как мне надо. А дальше то как отсортировать уже в дереве объекты?

Re: Версия 3.0.82 Метод Content.Move

Shoorup пишет:

+tdmQueryConditionVisible помогло
Но чета я с утра видимо туплю...

Получил я допустим из временной выборки значение атрибута которое мне надо (порядок кстати по мере создания объекта). Закинул значения в массив и отсортировал как мне надо. А дальше то как отсортировать уже в дереве объекты?

Я тоже с утра туплю, поэтому тупо предложу вставить их все в нужные места с помощью метода move:
content.Move objectArray(Index), Index
или что-то в этом духе

Re: Версия 3.0.82 Метод Content.Move

Sub List_BeforeShow(Sheet)
If ThisObject.ObjectDefName = "OBJECT_NSI_UKAZANIA_GTSS" Then

Dim ArrObjNoNSI()
For i=0 To Sheet.RowsCount-1
  ReDim Preserve ArrObjNoNSI(i)
  If InStr (Sheet.CellValue(i,0),"П - ")=7 Then
      ArrObjNoNSI(i)= Replace (Sheet.CellValue(i,0), "/", "/0000000") '1247/1П -> 1247/00000001П
  ElseIf InStr (Sheet.CellValue(i,0),"П - ")=8 Then
      ArrObjNoNSI(i)= Replace (Sheet.CellValue(i,0), "/", "/000000")  '1247/10П -> 1247/00000010П
  ElseIf InStr (Sheet.CellValue(i,0),"П - ")=9 Then
      ArrObjNoNSI(i)= Replace (Sheet.CellValue(i,0), "/", "/00000")   '1247/100П -> 1247/00000100П
  ElseIf InStr (Sheet.CellValue(i,0),"П - ")=10 Then
      ArrObjNoNSI(i)= Replace (Sheet.CellValue(i,0), "/", "/0000")    '1247/1000П -> 1247/00001000П
  ElseIf InStr (Sheet.CellValue(i,0)," - ")=7 Then
      ArrObjNoNSI(i)= Replace (Sheet.CellValue(i,0), "/", "/000")     '1247/1 -> 1247/0001
  ElseIf InStr (Sheet.CellValue(i,0)," - ")=8 Then      
      ArrObjNoNSI(i)= Replace (Sheet.CellValue(i,0), "/", "/00")      '1247/10 -> 1247/0010
  ElseIf InStr (Sheet.CellValue(i,0)," - ")=9 or InStr (Sheet.CellValue(i,0),"А - ")=9 Then
      ArrObjNoNSI(i)= Replace (Sheet.CellValue(i,0), "/", "/0")       '1247/100  или 1247/100A -> 1247/0100 или 1247/0100А
  Else
      ArrObjNoNSI(i)= Sheet.CellValue(i,0)                            '1247/1000 -> 1247/1000
  End If
Next

' Сортировка вставками
key=0 
temp=0
i=0
Do While i<Sheet.RowsCount-1
  key=i+1
  temp=ArrObjNoNSI(key)
    j=i+1
    Do While j>0
      if (temp<ArrObjNoNSI(j-1)) Then
        ArrObjNoNSI(j)=ArrObjNoNSI(j-1)
        'ThisObject.Content.Move (j-1), (j)
        key=j-1
      End If
    j=j-1
    Loop
ArrObjNoNSI(key)=temp
i=i+1
Loop
For i=0 To Sheet.RowsCount-1
  MsgBox ArrObjNoNSI(i)
Next

End If
End Sub

Где влепить этот Move и с какими индексами недогоняю:) Массив ArrObjNoNSI сортируется правильно и быстро. Осталось дело за малым - переставить объекты в дереве и обновить... но тут не знаю как...

Re: Версия 3.0.82 Метод Content.Move

В принципе - куда угодно. Если есть отсортированный массив, то осталось только по циклу провести расстановку объектов. Важно запросить сортируемую коллекцию в объект, потом можно воспользоваться методом Move
Что то вроде такого:

  Set ContentToSort = ThisObject.Content
  For i = 0 To UBound(ArrObjNoNSI) -1
    ContentToSort.Move ArrObjNoNSI(i), i
  Next
  ContentToSort.Update

(изменено: McZag, 17 октября 2014г. 11:05:43)

Re: Версия 3.0.82 Метод Content.Move

Shoorup пишет:

Где влепить этот Move и с какими индексами недогоняю:) Массив ArrObjNoNSI сортируется правильно и быстро. Осталось дело за малым - переставить объекты в дереве и обновить... но тут не знаю как...

Если позволите, пара замечаний по коду.
Стоимость сортировки вставками - O(n^2). Если у вас частично отсортированные данные и их мало - это хороший выбор. Если данных много и они идут вразнобой, это жуткий тормоз.

У вас нет операции выхода из цикла кроме его нормального завершения, поэтому ReDim Preserve ArrObjNoNSI(i) надо вынести за тело цикла: ReDim Preserve ArrObjNoNSI(Sheet.RowsCount)

Вы в цикле много раз вычисляете две одинаковых функции InStr (Sheet.CellValue(i,0),"П - ") и  InStr (Sheet.CellValue(i,0)," - "). Посчитаете их один раз  ;), это в среднем по больнице будет быстрее.

(изменено: Shoorup, 17 октября 2014г. 11:56:54)

Re: Версия 3.0.82 Метод Content.Move

Спасибо за замечания и подсказки.
Объекты уже отсортированы и сортировать надо будет вновь добавляемые объекты (обычно около 20).
Скрипт еще только на стадии бета, поэтому всякие нюансы еще не учитывались.

Единственное меня сейчас смущает вот что. Конечный массив ArrObjNoNSI() содержит просто string - наименования объектов. А должны содержать, как я понимаю, индексы по которым они сейчас расположены в дереве и только тогда сработает код:

Set ContentToSort = ThisObject.Content
  For i = 0 To UBound(ArrObjNoNSI) -1
    ContentToSort.Move ArrObjNoNSI(i), i
  Next
  ContentToSort.Update

Вопрос тогда как мне сформировать массив индексов (или возможно что-то другое) отсортированных объектов?