Как посчитать повторяющиеся записи в OpenOffice/LibreOffice BASIC?

У меня огромное количество данных на многих листах в LibreOffice - ADDRESS колонна и DATA столбец - и я хотел бы посчитать, сколько раз каждый адрес встречается, положить в NUM_ADDR колонка. Например:

ADDR             | DATA             | NUM_ADDR
00000000bbfe22d0 | 876d4eb163886d4e | 1
00000000b9dfffd0 | 4661bada6d4661ba | 1
00000000b9dfc3d0 | 5d4b40b4705d4b40 | 1
00000000b9def7d0 | 8f8570a5808f8570 | 1
00000000b9de17d0 | 63876d4eb163886d | 1
00000000b9dddfd0 | 6d4eb163886d4eb1 | 3
00000000b9dddfd0 | 705d4b40b4705d4b | 
00000000b9dddfd0 | b4705d4b40b4705d | 
00000000b7df83d0 | 40b4705d4b40b470 | 1
00000000b7d607d0 | 705d4b40b4705d4b | 1
...

Делая вещи вручную, я использовал COUNTIF функция на каждом адресе, но я обнаружил, что макрос сэкономит время в долгосрочной перспективе. Вот фрагмент того, что у меня есть, учитывая, что предыдущая функция уже определила длину (количество строк) данных, хранящихся в RowCounter:

Dim CountedAddr(RowCounter, RowCounter) as String
Dim CountedAddrPtr as Integer
Dim CurrentCell as Object
Dim i as Integer

CountedAddrPtr = 0

' Populate CountedAddr array
For i = 1 to RowCounter-1
  CurrentCell = CurrentSheet.getCellByPosition(0, i)
  If Not CurrentCell.String In CountedAddr(?) Then
    CurrentSheet.getCellByPosition(2, i).Value = 1 ' for debugging
    CountedAddr(CountedAddrPtr, 0) = CurrentCell.String
    CountedAddrPtr = CountedAddrPtr + 1
  Else
    CurrentSheet.getCellByPosition(2, i).Value = 0 ' for debugging
  EndIf
Next

' For each unique address, count number of occurances
For i = 0 to UBound(CountedAddr())
  For j = 1 to RowCounter-1
    If CurrentSheet.getCellByPosition(0, j).String = CountedAddr(i, 0) Then
      CountedAddr(i, 1) = CountedAddr(i, 1)+1
    EndIf
  Next
Next

' Another function to populate NUM_ADDR from CountedAddr array...

Итак, мой первый вопрос: как мы можем определить, находится ли элемент (адрес в текущей ячейке) в CountedAddr массив (см. (?) выше)? Во-вторых, есть ли гораздо более эффективный способ достижения второго блока кода? К сожалению, об сортировке не может быть и речи, поскольку хронология адресов и данных образует нечто временное. В-третьих, является ли весь шебанг глупым способом решения этой проблемы?

Большое спасибо от аппаратного dood на программную задачу!

1 ответ

Решение

Объекты словарного типа, такие как VB6 Collection, эффективны для поиска элементов, потому что он находит ключ напрямую, а не просматривает длинный массив. наш countedAddrs Коллекция ниже будет хранить счетчик для каждого адреса.

Sub CountAddrs
    Dim countedAddrs As New Collection
    Dim oCurrentSheet As Object
    Dim oCurrentCell As Object
    Dim currentAddr As String
    Dim i As Integer
    Dim newCount As Integer
    Dim rowCounter As Integer
    Const ADDR_COL = 0
    Const COUNT_COL = 2

    oCurrentSheet = ThisComponent.CurrentController.ActiveSheet
    rowCounter = 11
    ' Populate countedAddrs array.
    For i = 1 to rowCounter - 1
      oCurrentCell = oCurrentSheet.getCellByPosition(ADDR_COL, i)
      currentAddr = oCurrentCell.String
      If Contains(countedAddrs, currentAddr) Then
        ' Increment the count.
        newCount = countedAddrs.Item(currentAddr) + 1
        countedAddrs.Remove(currentAddr) 
        countedAddrs.Add(newCount, currentAddr)
        oCurrentSheet.getCellByPosition(COUNT_COL, i).Value = newCount ' for debugging
      Else
        countedAddrs.Add(1, currentAddr)
        oCurrentSheet.getCellByPosition(COUNT_COL, i).Value = 1 ' for debugging
      EndIf
    Next
End Sub

Этот код требует следующую вспомогательную функцию. В большинстве языков словарные объекты имеют эту встроенную функциональность, но Basic довольно прост.

' Returns True if the collection contains the key, otherwise False.
Function Contains(coll As Collection, key As Variant)
    On Error Goto ErrorHandler
    coll.Item(key)
    Contains = True
    Exit Function
ErrorHandler:
    Contains = False
End Function
Другие вопросы по тегам