Sorting a Scriptural Index – Take I
Update: Please see my next try at this. If you mess up the count of instances of “scripture index” in the rather unhandy way I found the scripture index you could get some nasty results. The new one fixes that.
I have finally gotten the first draft of the little routine I mentioned earlier for sorting a scriptural index. The process is fairly simple. You create a user-defined index, set it up as you want, but do not check the box to protect the index from changes. The user-defined index will gather your references for you. Simply mark scripture references in your text as desired, then generate the index.
The routine assumes it is labeled “Scripture Index” and also has an entry in the Table of Contents. See the comments for where to change the code if this is different in your document.
I’ve included an open document text file with the macros included, and also included them in this entry. Do not update the index in the sample document, since the actual text with the index marks is not included.
One last note. I’m just enjoying the process of learning about OO Basic, and this routine qualifies as very much Alpha. It has already sorted my index to my satisfaction, but don’t plan to use it unless you can modify it as you need to. I will certainly improve on it over time–at least I hope so.
REM ***** BASIC *****
REM ***** Sort Scripture Inde *****
REM ***** Henry Neufeld, https://hneufeld.com *****
REM ***** Feel free to use whatever of this code you find useful *****
'stuff is the workhorse array for this. 500 entries is arbitrary. In the future I plan
'to have the routine redim if the array is too small. For now 500 entries is quite adequate
Global stuff(500,6) as Variant '0 = paragraph text; 1=Object; 2=Page; 3=Passage
Const S_OBJ = 0 ' The TextElement Object
Const S_TEXT = 1 ' The actual text of the object; I swap the text between objects to sort
Const S_PAGE = 2 ' The page number from the line, used for the final sort
Const S_PASSAGE = 3 'used to detect identical entries so I can format duplicates
Const S_BOOK = 4 'S_BOOK, S_CHAPTER, and S_VERSE are used solely for sorting
Const S_CHAPTER = 5
Const S_VERSE = 6
Dim Bible() as String
Sub Main
End Sub
'Create the array of Bible books. Modify this array to use different sort orders, add the apocrypha, and so forth
'This could be more efficient than a sequential search, but since I only use the routine a few times in the lifetime
'of any manuscript the difference in speed will be unimportant
Sub InitializeBible
Bible = Array("Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy", "Joshua", "Judges", "Ruth", "1 Samuel", "2 Samuel", "1 Kings", "2 Kings", _
"1 Chronicles", "2 Chronicles", "Ezra", "Nehemiah", "Esther", "Job", "Psalm", "Proverbs", "Ecclesiastes", "Song of Solomon", _
"Isaiah", "Jeremiah", "Lamentations", "Ezekiel", "Daniel", "Hosea", "Joel", "Amos", "Obadiah", "Jonah", "Micah", "Nahum", "Habakkuk", _
"Zephaniah", "Haggai", "Zechariah", "Malachi", "Matthew", "Mark", "Luke", "John", "Acts", "Romans", "1 Corinthians", "2 Corinthians", _
"Galatians", "Ephesians", "Philippians", "Colossians", "1 Thessalonians", "2 Thessalonians", "1 Timothy", "2 Timothy", "Titus", _
"Philemon", "Hebrews", "James", "1 Peter", "2 Peter", "1 John", "2 John", "3 John", "Jude", "Revelation")
End Sub
'Sequentially searches the list of Bible books to conver the book into a number for sort purposes
Function BookNumber(bk as String) as Integer
Dim i as Integer
If bk = "" then
BookNumber = 67
Exit Function
End If
for i=0 to 65
if Bible(i) = bk then Exit For
next i
BookNumber = i
End Function
'Splits the line of text into the elements of the "stuff" array
Sub SplitText(i as Integer)
Dim txt,book,chap,verse as String
dim c as Integer
txt = stuff(i,S_TEXT)
c = instr(txt,chr(9))
'Alternatively the string will be empty or a completely invalid reference.
If c > 0 Then
stuff(i,S_PAGE) = Val(Mid(txt,c+1))
txt = Left(txt,c-1)
stuff(i,S_PASSAGE) = txt
Else
stuff(i,S_PAGE) = 0
End If
c = instr(3,txt," ")
If c > 0 then
book = Left(txt,c-1)
Else
book = ""
End If
txt = Mid(txt,c+1)
stuff(i,S_BOOK) = BookNumber(book)
c = instr(txt,":")
if c > 0 then
stuff(i,S_CHAPTER) = Val(Left(txt,c-1))
txt = Mid(txt,c+1)
c = instr(txt,"-")
if c = 0 then c = instr(txt,"f")
if c <> 0 then txt = left(txt,c-1)
stuff(i,S_VERSE) = Val(txt)
else
stuff(i,S_CHAPTER) = Val(txt)
stuff(i,S_VERSE) = 0
end if
End Sub
'In sorting I swap everything but the TextElement object, then put the sorted strings into the unsorted
'TextElement objects to create the fully sorted list
Sub Swap(i as Integer,j as Integer)
Dim temp(6)
Dim k as Integer
'code to swap two elements of stuff here
for k = 1 to 6
temp(k) = stuff(i,k)
next k
for k=1 to 6
stuff(i,k) = stuff(j,k)
next k
for k = 1 to 6
stuff(j,k) = temp(k)
next k
End Sub
'The main routine. Run this from the Macros menu (or create whatever means of starting it you wish)
Sub Paragraphs
Dim Doc As Object
Dim Enum As Object
Dim TextElement As Object
Dim p, cnt, idx,k as Integer
Dim psg, txt as String
InitializeBible()
Doc = StarDesktop.CurrentComponent
Enum = Doc.Text.createEnumeration
p = 0
cnt = 0
idx = 0
While Enum.hasMoreElements
TextElement = Enum.nextElement
If TextElement.supportsService("com.sun.star.text.Paragraph") Then
if p = 3 then
cnt = cnt + 1
stuff(idx,S_TEXT) = TextElement.String
stuff(idx,S_OBJ) = TextElement
SplitText(idx)
idx = idx + 1
end if
'This is a kind of bullheaded approach. I know that there will be either 2 or 3 instances
'of "Scripture Index" in the document before the first entry. Adjust the statement "If p = 3"
'if this number is different.
if InStr(TextElement.String,"Scripture Index") > 0 Then
p = p + 1
End If
End If
Wend
k = idx - 1
'Sorts the list
QSort(0,k)
'Sets the sorted strings into the correct TextElements and blanks out the passage on multiple entries
'for the same passage
psg = ""
for p=0 to k
If psg <> stuff(p,S_PASSAGE) Then
psg = stuff(p,S_PASSAGE)
Else
txt = stuff(p,S_TEXT)
txt = Mid(txt,InStr(txt,Chr(9)))
stuff(p,S_TEXT) = txt
End If
stuff(p,S_OBJ).String = stuff(p,S_TEXT)
next p
MsgBox "Finished"
End Sub
'Comparison based on book number, then chapter, then verse, then the text of the passage reference
'which orders verses like Psalm 8:5-7 or 2 Timothy 3:16ff, then finally by the page number
Function Comp(i as Integer, j as Integer) as Integer
if stuff(i,S_BOOK) <> stuff(j,S_BOOK) then
Comp = stuff(i,S_BOOK) - stuff(j,S_BOOK)
Exit Function
End If
if stuff(i,S_CHAPTER) <> stuff(j,S_CHAPTER) then
Comp = stuff(i,S_CHAPTER) - stuff(j,S_CHAPTER)
Exit Function
End If
If stuff(i,S_VERSE) <> stuff(j,S_VERSE) Then
Comp = stuff(i,S_VERSE) - stuff(j,S_VERSE)
Exit Function
End If
If stuff(i,S_PASSAGE) > stuff(j,S_PASSAGE) Then
Comp = 1
Exit Function
ElseIf stuff(i,S_PASSAGE) < stuff(j,S_PASSAGE) Then
Comp = -1
Exit Function
End If
Comp = stuff(i,S_PAGE) - stuff(j,S_PAGE)
End Function
'I use a QSort just because I had one I'd written in C++ to convert.
Sub QSort(left as Integer, right as Integer)
Dim i, last as Integer
if left >= right Then Exit Sub
Swap(left,(left+right)/2)
last = left
for i=left+1 to right
if Comp(left,i) > 0 then
last = last + 1
Swap(last,i)
end if
next i
Swap(left,last)
QSort(left,last-1)
QSort(last+1,right)
End Sub
Oh, in order to use the macro, run “Paragraphs.” “Main” doesn’t do anything.
The sample document with macro is here: scripture index sort.