Word 2010 BA to shuffle numbered lists - vba

I am trying to shuffle the items in a numbered list in MS Word 2010. The background for this question is that my wife is an English teacher who makes her tests using Word. Whenever she makes a test she also makes a second version by changing the order of the items in the numbered lists.
I am looking to either:
change the order of items in a numbered list that I select using the mouse, ie select the numbered list, push a button/shortcut and the list is shuffled
or
change the order of all numbered lists in the test, ie the macro looks for the start of a new numbered list, selects all items in the list, changes the order of the items and then moves over to the next numbered list.
All lists should keep the same formatting (ie start number) after using the code.
I tried for the first instance but did not succeed in determining the start and end line numbers of my selection.
Example:
Original:
===== Start: ========
Question 1 What answer is correct?
Answer A
Answer B
Answer C
Question 2 What answer is correct?
Answer D
Answer E
Answer F
Question 3 What answer is correct?
Answer G
Answer H
Answer J
======End========
The macro should create this:
======Start========
Question 1 What answer is correct?
Answer C
Answer A
Answer B
Question 2 What answer is correct?
Answer F
Answer E
Answer D
Question 3 What answer is correct?
Answer H
Answer J
Answer G
====End======

Since you are dealing with only 3 list items, it is pretty easy. Just swap any of the two items. The following code does the same.
For more than 3 items, you may have to repeat the logic of swapping more rows. But you should get the basic idea about how to go about it from this code.
Sub Shuffle()
Dim li As List, rng As Range, random As Integer
Randomize
For Each li In ThisDocument.Lists
' get either 1 or 2. We will swap this with the 3rd item
random = CInt(Rnd + 1)
' add a new paragraph as temporary place holder. This is so that we can keep the paragraph with its formatting intact.
Set rng = li.Range.Paragraphs.Add.Range
rng.FormattedText = li.Range.Paragraphs(random).Range.FormattedText
' swap the items
li.Range.Paragraphs(random).Range.FormattedText = li.Range.Paragraphs(3).Range.FormattedText
li.Range.Paragraphs(3).Range.FormattedText = rng.FormattedText
' remove the temporary paragraph we added
li.Range.Paragraphs.Last.Range.Delete
Next
End Sub

I slightly modified the code by Pradeep Kumar and this works like a charm, even with an unknown number of items per numbered list and so that it can be incorporated in the normal.dot template:
Sub Shuffle()
Dim li As List, rng As Range, random As Integer, nbr As Integer
Application.ScreenUpdating = False
Randomize
For Each li In ActiveDocument.Lists
nbr = li.CountNumberedItems
' Run along all items in list and swap with a random one from the same list
For a_counter = 1 To nbr
' Make sure the item is not swapped with itself, that would fail
again:
random = CInt((nbr - 1) * Rnd + 1)
If random = a_counter Then GoTo again
' add a new paragraph as temporary place holder. This is so that we can keep the paragraph with its formatting intact.
Set rng = li.Range.Paragraphs.Add.Range
rng.FormattedText = li.Range.Paragraphs(random).Range.FormattedText
' swap the items
li.Range.Paragraphs(random).Range.FormattedText = li.Range.Paragraphs(a_counter).Range.FormattedText
li.Range.Paragraphs(a_counter).Range.FormattedText = li.Range.Paragraphs(nbr + 1).Range.FormattedText
' remove the temporary paragraph we added
li.Range.Paragraphs(nbr + 1).Range.Delete
Next a_counter
Next
Application.ScreenUpdating = True
End Sub

Related

Superscript Formatting Erased when Text is stored in String

Dim ST As String
ST = ActiveDocument.Paragraphs(1).Range.Text
In my document, Paragraphs(1) is actually 2 + 32. However, with Debug.Print ST, the output is 2 + 32. Is there any way to store the data without compromising the superscript and subscript formatting?
The objective behind this is to store 5 lines in ST(1 to 5) and then shuffle the order of the 5 lines.
1 - It is not clear how do you want to capture the paragraphs so I'm assuming that you will have those paragraphs selected, modify it based on your requirement
2 - It is also not clear on what shuffle means so I will assume that you want it to be reversed, you will need to come out with your own logic on how to shuffle the paragraphs:
FormattedText property can be used to replace a range with formatted text so this should work for you:
Private Sub ShuffleSelectedParagraphs()
ActiveDocument.Content.InsertParagraphAfter
Dim i As Long
For i = Selection.Paragraphs.Count To 1 Step -1
ActiveDocument.Content.Paragraphs.Last.Range.FormattedText = Selection.Paragraphs(i).Range.FormattedText
Next
End Sub
You will need to select the paragraphs first then run the Sub, it will duplicate the selected paragraphs at the end of the document but in the reverse order.

Getting values from a multi column listbox VBA

I am in need of help, I am trying to get all three columns of my multi column list box in a for loop, it should be pretty simple but I cannot figure it out,
I have googled for hours and to be honest haven't gotten anywhere as everyone wants it to be able to select and then loop through where as i just want to loop through all of them and take them to separate strings,
Hope this makes sense,
Thank you,
You can cycle through entries in the list by using the script below and fill a new array which can be pasted to an assigned range on the sheet of the same size as the array
dim arrayctr as integer, i as integer, myarray() as variant
With mylistbox
'below sizes array to size of list in listbox.
'use arrctr to populate selected items to array without blank entries in between.
redim myarray(1 to .listcount, 1 to 1) as variant
'.listcount gives total entries, but list starts from 0 so use i-1 to get values
For i = 1 to .listcount
if .selected(i-1) = true then myarray(arrayctr) = .list(i-1): _
arrctr = arrctr + 1
Next i
End with
you can use For next loop through entries and use Exit For when the value of the array = "" so it handles only entries with data in and avoids pointless calculations.
I have had similar troubles with finding online answers to some questions such as this, I hope it helps!

How to use variables in regular MS excel expressions

My research shows that I need to use Visual Basic. I am a programmer/developer, but have never used VB so if anyone could dumb it down it would be appreciated.
Here's my working excel function:
=IF(MATCH(1,E1:DP1,0),D1,FALSE)
I want to loop a few of those numbers such that:
=IF(MATCH(141,E1:DP378,0),D378,FALSE)
THEN take my answers (which will be strings, because column D are all strings, the rest of the excel file are numbers)
=CONCAT
end goal: have 141 String arrays populated based on the data in my table.
I went ahead and made my first attempt at VBA like this:
Sub myFunc()
'Initialize Variables
Dim strings As Range, nums As Integer, answer() As Variant, listAnswers() As Variant
'set variables
strings = ("C1:C378")
nums = 141
i = 0
j = 0
ReDim Preserve answer(i)
ReDim Preserve listAnswers(j)
'answer() = {""}
'for each in nums
For counter = 0 To nums
ReDim Preserve listAnswers(0 To j)
'set each list of answers
listAnswers(i) = Join(answer(), "insertJSONcode")
j = j + 1
'for each in Stings
For Each cell In strings
If cell <> "" Then
ReDim Preserve answer(0 To i)
answer(i) = 'essentially this: (MATCH(2,E1:DP1,0),D1,FALSE)
i = i + 1
end If
next cell 'end embedded forEach
Next LCounter 'end for loop
'is this possible? or wrong syntax?
Range("A:A").Value = listAnswers() ' should print 141 arrays from A1 to A141
End Sub
EDIT:
Important note I do NOT need to call the sheet by Name. I've successfully written integer values to by excel sheet in column A without doing so.
Also, the VBA I wrote I was never intended to work, I know it's broken at least where answer(i) is supposed to write to something. I'm only putting that code there to show I was able to at least able to get into spitting distance of the proper logic and prove I've put some serious effort into solving the problem and give a rough starting point.
Here's an image of the excel format. Column C goes down to 378 and the numbers listed from E through DP are populated by a database. It consists of blank cells and numbers between 1 and 141.
Looking back at my if statement:
=IF(MATCH(2,E2:DP2,0),D2,FALSE)
If I were to type that exactly into cell B2 it would output the correct answer "text2". which is neat and all, but I need every instance of text 2 written out, then CONCAT those results. Easy so far, I could drag that down all the way through column B and have all of my "text" strings in one column, CONCAT that column and there's the answer. However I don't just need #2, I need each number between 1 and 141. Plus I want to avoid writing 141 columns with a CONCAT on top of each one.

Macro query spread over multiple-sheets

Wording my question is slightly tricky so I've included screen-shots to make this easier. I have 2 separate spreadsheets which are currently not linked together in anyway. What I've been asked to do is:
For the drop-downs which have a * next to them, have this * drop-down get converted into a acronym (I.e. If it's Home Visit *, then this will be converted to HV), and have it automatically entered into Cell Position X. Please refer to Image 1 then Image 2)
So the user would click on Sheet one, select the relevant drop-down field and then assign how much time that task took. The second sheet would then update itself with this information - it would insert the users name, program and activities. This is where it gets very tricky. Based off the drop-down selection, if it is asterisked (*), then based off the field-type it will convert it into a set acronym which would then be placed in one of the data fields based off the entry date that has been provided.
I designed both spread-sheets and they have macros in the background, but I can't seem to work out how to best perform this. Would you suggest a transpose function which checks firstly the date criteria and then an INDEX(MATCH) function to match the criteria against a pre-defined name-range which converts Home Visit etc. to HV automatically? I'm also unsure of how to insert delimiters for each new entry that is read. If anyone can provide help I would be very grateful.
I'm not 100% sure I understand your question, but here goes:
What about adding a Worksheet_Change event to look for changes in the drop-down's cell, and then converting it to an acronym?
Place the following code inside the sheet of interest:
Private Sub Worksheet_Change(ByVal Target As Range)
'If Cell A1 is changed, put the acronym into A2
If Target.Row = 1 And Target.Column = 1 Then
Cells(2, 1) = GetAcronym(Target.Value)
End If
End Sub
Function GetAcronym(TheText As String) As String
Dim result As String
Dim x As Long
'Always grab the first letter
result = Mid(TheText, 1, 1)
'Get the other letters
For x = 2 To Len(TheText) - 1
If Mid(TheText, x, 1) = " " Then result = result & Mid(TheText, x + 1, 1)
Next x
GetAcronym = UCase(result)
End Function

Fill Series, a lot of Series's?

In Excel I've got sequential box numbers in column B, and each box has a couple dozen files that need sequential-by-box place numbers in column C. The way I usually do this is to Fill Series down a selection (selected by hand) of all the cells for that box in Column C, which is fine if you've got a few boxes to do, but now I have several hundred.
[I've got a 394x290 example screenshot I was going to include to show what I mean, but since this is my first post I don't have enough rep, sorry -- link to it on g+ here.]
I thought I could put some VBA code into a macro to select the contiguous cells with the same box number, offset one column right [Offset (0, 1), yeah?], fill series those cells from 1, and go on to the next box. But I haven't had any luck finding anything similar that's been done, nor have I been able to get anything I've looked up to work for this. (Not surprising since I rarely try VBA, hopefully my question's not too noobish for this site.)
From what I can tell, you want the Plc column to fill up series starting from 1 for the same Box Num.
There may exist a fast and quick way but simple method is to go through the rows. Try below:
Sub FillUpPlc()
Dim oRng As Range, n As Long ' n used for series filling
Application.ScreenUpdating = False
n = 1
Set oRng = Range("B2")
Do Until IsEmpty(oRng)
' Increment n if it's same as cell above, otherwise reset to 1
If oRng.Value = oRng.Offset(-1, 0).Value Then
n = n + 1
Else
n = 1
End If
oRng.Offset(0, 1).Value = n ' Store n to next column
Set oRng = oRng.Offset(1, 0) ' Move to next row
Loop
Set oRng = Nothing
Application.ScreenUpdating = True
End Sub
No need to break out the VBA. This can be done with a formula. Starting in C2 and copied down
=IF(B2<>B1,1,C1+1)
Much, much faster than VBA looping through thousands of rows.