Sorting a collection by value - excel

I have a collection of dates with the format "1801,1802,1803,1710,1711 etc.
I would like to be able to sort them by value so that the lowest ones are first e.g 1710,1711,1801,1802,1803.
The reason for this is so i can then put them into a string in the correct order.
I am not sure how to do this and any help would be appreciated. Thank you

Here is a really simple example:
Sub Nathan()
Dim c As Collection, arr, brr
Dim MyString As String
Set c = New Collection
' Part 1 build Collection
arr = Split("1801,1802,1803,1710,1711", ",")
For Each a In arr
c.Add Val(a)
Next a
' Part 2 put Collection into an array
ReDim brr(1 To c.Count)
For i = 1 To c.Count
brr(i) = c.Item(i)
Next i
' Part 3 sort the array
Call aSort(brr)
' make and output a css
MyString = Join(brr, ",")
MsgBox MyString
End Sub
Public Sub aSort(ByRef InOut)
Dim i As Long, j As Long, Low As Long
Dim Hi As Long, Temp As Variant
Low = LBound(InOut)
Hi = UBound(InOut)
j = (Hi - Low + 1) \ 2
Do While j > 0
For i = Low To Hi - j
If InOut(i) > InOut(i + j) Then
Temp = InOut(i)
InOut(i) = InOut(i + j)
InOut(i + j) = Temp
End If
Next i
For i = Hi - j To Low Step -1
If InOut(i) > InOut(i + j) Then
Temp = InOut(i)
InOut(i) = InOut(i + j)
InOut(i + j) = Temp
End If
Next i
j = j \ 2
Loop
End Sub
Consider using a scripting dictionary (because sort is already built-in)
EDIT#1:
To avoid writing a sort, you can use System.Collection.ArrayList. Here is an example that takes data from column A, sorts it, and puts the result in column B:
Sub AsortedAffair()
Dim arr, o As Object, oc As Long, i As Long
arr = Columns(1).SpecialCells(2)
Set o = CreateObject("System.Collections.ArrayList")
For Each a In arr
o.Add a
Next a
o.Sort
oc = o.Count
For i = 1 To oc
Cells(i, 2) = o.Item(i - 1)
Next i
End Sub
It is equally easy to build a comma-separated string out of this.
Note that the index of this Item() property starts at zero rather than one.

Related

Excel Picking random numbers with IF statement and creating random arrays

I'm trying to create a list of random numbers that don't duplicate and then I want to take those numbers and generate multiple lists that are all random.
for example
for cells A1-A10 i want random numbers from 1-100
and then for A11-20 I want random numbers from 1-100
and then for A21 - A30 I want random number from 1-100
dragging down and repeating this seems possible with the MOD and OFFSET function which I'm still learning about.
For generating numbers I've tried
=RANDBETWEEN(1,100)
but it produces duplicates.
Ive also tried
Putting
Rand()
in G1 and generating random numbers
and then using
=RANK.EQ(G1,$G$1:$G$100)
along with:
=INDEX($H$1:$H$100, RANK(G1,$G$1:$G$100), 1)
which is better and doesn't produce duplicates but since i need multiple lists what happens is every list has the exact same data since the code is just replicating the same reference.
e.g : what i get is A1-10
A1 = 65
A2 = 54
A3 = 23
A4 = 31
A10= 23
then i try repeat the code for A11 - A 20
but it produces the same values.
even if i use
=INDEX($H$1:$H$50, RANK.EQ(G2, $G$1:$G$100) + COUNTIF($G$1:G2, G2) - 1, 1)
and then i have
=INDEX($H$1:$H$100, RANK(G1,$G$1:$G$100), 1)
in two differnt rows they produce the exact same random numbers
So i reckoned I need something that randomises every time.
Any help is appreciated
Try the following VBA macro:
Sub vRandom()
Dim mn As Long, mx As Long, samples As Long
Dim times As Long, arr1(), t As Long, s As Long
Dim k As Long
mn = 1
mx = 100
samples = 10
times = 3
k = 1
ReDim arr1(mn To mx)
For s = mn To mx
arr1(s) = s
Next s
For t = 1 To times
Call Shuffle(arr1)
For s = 1 To samples
Cells(k, 1) = arr1(mn + s - 1)
k = k + 1
Next s
Next t
End Sub
Public Sub Shuffle(InOut() As Variant)
Dim i As Long, J As Long
Dim tempF As Double, Temp As Variant
Hi = UBound(InOut)
Low = LBound(InOut)
ReDim Helper(Low To Hi) As Double
Randomize
For i = Low To Hi
Helper(i) = Rnd
Next i
J = (Hi - Low + 1) \ 2
Do While J > 0
For i = Low To Hi - J
If Helper(i) > Helper(i + J) Then
tempF = Helper(i)
Helper(i) = Helper(i + J)
Helper(i + J) = tempF
Temp = InOut(i)
InOut(i) = InOut(i + J)
InOut(i + J) = Temp
End If
Next i
For i = Hi - J To Low Step -1
If Helper(i) > Helper(i + J) Then
tempF = Helper(i)
Helper(i) = Helper(i + J)
Helper(i + J) = tempF
Temp = InOut(i)
InOut(i) = InOut(i + J)
InOut(i + J) = Temp
End If
Next i
J = J \ 2
Loop
End Sub
The code produces 3 blocks of items (10 items in each block).
Within a block there are no duplicates, but there may be duplicates between blocks.

Function to find 2nd and 3rd most common text string where blank cells are present

I am trying to use a formula to show the 1st, 2nd, and 3rd most common text string in a column. This formula works but only if I designate a specific range with no blank cells. The issue with this is that the list is often updated by adding a line at the bottom so the range needs to be dynamic (or the entire column which is what I am trying to do).
=IFERROR(INDEX(C:C,MODE(IF(COUNTIF(U$1:U1,C:C)=0,MATCH(C:C,C:C,0)+{0,0}))),"")
Any insight is greatly appreciated.
First enter this VBA code in a standard module:
Public Function MostCommon(rng As Range) As Variant
Dim rng2 As Range, r As Range, C As Collection, arr(), arr2
Dim cKount As Long, i As Long, Kaller As Range, HowBig As Long
Set rng2 = Intersect(rng, rng.Parent.UsedRange)
Set C = New Collection
Set Kaller = Application.Caller
For Each r In rng2
If r.Value <> "" Then
On Error Resume Next
C.Add r.Value, CStr(r.Value)
On Error GoTo 0
End If
Next r
cKount = C.Count
ReDim arr(1 To cKount, 1 To 2)
For i = 1 To cKount
arr(i, 1) = C.Item(i)
arr(i, 2) = Application.WorksheetFunction.CountIf(rng2, arr(i, 1))
Next i
Call VBA_Sort(arr)
HowBig = Application.WorksheetFunction.Max(cKount, Kaller.Rows.Count)
ReDim arr2(1 To HowBig, 1 To 2)
For i = 1 To HowBig
arr2(i, 1) = ""
arr2(i, 2) = ""
Next i
For i = 1 To cKount
arr2(i, 1) = arr(i, 1)
arr2(i, 2) = arr(i, 2)
Next i
MostCommon = arr2
End Function
Public Sub VBA_Sort(InOut())
Dim i As Long, J As Long, Low As Long, _
Hi As Long, Temp As Variant
Low = LBound(InOut, 1)
Hi = UBound(InOut, 1)
J = (Hi - Low + 1) \ 2
Do While J > 0
For i = Low To Hi - J
If InOut(i, 2) < InOut(i + J, 2) Then
Temp = InOut(i, 2)
InOut(i, 2) = InOut(i + J, 2)
InOut(i + J, 2) = Temp
Temp = InOut(i, 1)
InOut(i, 1) = InOut(i + J, 1)
InOut(i + J, 1) = Temp
End If
Next i
For i = Hi - J To Low Step -1
If InOut(i, 2) < InOut(i + J, 2) Then
Temp = InOut(i, 2)
InOut(i, 2) = InOut(i + J, 2)
InOut(i + J, 2) = Temp
Temp = InOut(i, 1)
InOut(i, 1) = InOut(i + J, 1)
InOut(i + J, 1) = Temp
End If
Next i
J = J \ 2
Loop
End Sub
Then select a two-column block (like E1 through F50) and array-enter the following:
=MostCommon(C:C)
As you see, the function returns a short frequency table with the most frequent items at the top.
Array formulas must be entered with Ctrl + Shift + Enter rather than just the Enter key. If this is done correctly, the formula will appear with curly braces around it in the Formula Bar.

Send numbers to other cells in excel

well, i write a code for get All Possible Combinations Of a number in 4 digit
and worked well, but i want send each number to opposite Cells
Dim N As Long, ary(1 To 4) As Long
N = Application.InputBox(prompt:="enter value", Type:=1)
xxx = 1
For i = 0 To N
For J = 0 To N
For k = 0 To N
For l = 0 To N
If i + J + k + l = N Then
ary(1) = i
ary(2) = J
ary(3) = k
ary(4) = l
Call LittleSort(ary())
Cells(xxx, 1).Value = "'" & ary(1) & ary(2) & ary(3) & ary(4)
xxx = xxx + 1
End If
Next l
Next k
Next J
Next i
Range("A:A").RemoveDuplicates Columns:=1, Header:=xlNo
End Sub
Public Sub LittleSort(ByRef InOut)
Dim i As Long, J As Long, Low As Long
Dim Hi As Long, Temp As Variant
Low = LBound(InOut)
Hi = UBound(InOut)
J = (Hi - Low + 1) \ 2
Do While J > 0
For i = Low To Hi - J
If InOut(i) > InOut(i + J) Then
Temp = InOut(i)
InOut(i) = InOut(i + J)
InOut(i + J) = Temp
End If
Next i
For i = Hi - J To Low Step -1
If InOut(i) > InOut(i + J) Then
Temp = InOut(i)
InOut(i) = InOut(i + J)
InOut(i + J) = Temp
End If
Next i
J = J \ 2
Loop
End Sub
and worked well, but i want send each number to opposite Cells
like this
write this code for do that
Sub Text_To_Cols()
Dim ary()
Dim Num As Long, i As Long
Num = Len(Range("A1").Value) - 1
ReDim ary(0 To Num)
For i = 0 To Num
ary(i) = Array(i, 1)
Next i
Range("A1").TextToColumns Destination:=Range("B1"), _
DataType:=xlFixedWidth, FieldInfo:=ary
End Sub
My problem is that my 4 numbers are divided into 5 numbers
how can i do that?

Adding a new Worksheet in its Alphabetically correct position

I have a Workbook with a number of Worksheets, currently 6 but this will increase in the future. The Tabs / Worksheet names are in Alphabetical order, with a "Template" sheet at the end.
I am cloning that Template sheet & creating a new Worksheet with the name specified by the user, but how can I insert it into the Workbook so it is in the correct position Alphabetically ?
I understand the workbook is already sorted and that a new sheet must be inserted in the alphabetical order, copied from a template sheet. The following code will do that:
Sub InsertSheet(name As String)
Dim i
For i = 1 To ActiveWorkbook.Sheets.Count
If ActiveWorkbook.Sheets(i).name >= name Then
Exit For
End If
Next i
ActiveWorkbook.Sheets("Template").Copy before:=ActiveWorkbook.Sheets(i)
ActiveSheet.name = name ' can fail if sheet already exists
End Sub
As an alternative. To sort the sheets after a new worksheet has been added:
Sub SheetSorter()
Dim ary() As String, I As Long
ReDim ary(1 To Sheets.Count)
For I = 1 To Sheets.Count
ary(I) = Sheets(I).Name
Next I
Call VBA_Sort(ary)
For Each a In ary
Sheets(a).Move after:=Sheets(Sheets.Count)
Next a
End Sub
Public Sub VBA_Sort(InOut() As String)
Dim I As Long, J As Long, Low As Long, _
Hi As Long, Temp As Variant
Low = LBound(InOut)
Hi = UBound(InOut)
J = (Hi - Low + 1) \ 2
Do While J > 0
For I = Low To Hi - J
If InOut(I) > InOut(I + J) Then
Temp = InOut(I)
InOut(I) = InOut(I + J)
InOut(I + J) = Temp
End If
Next I
For I = Hi - J To Low Step -1
If InOut(I) > InOut(I + J) Then
Temp = InOut(I)
InOut(I) = InOut(I + J)
InOut(I + J) = Temp
End If
Next I
J = J \ 2
Loop
End Sub

Splitting the data into 3 parts randomly - Excel

I have a dataset in an Excel sheet and i need to RANDOMLY split this (for instance 999 records) into 3 equal (and no duplicates) Excel files. Can this be done simply by using some Excel function or I need to write code to specifically do this?
Sometimes low-tech is best. If you don't need to repeat this very frequently...
add a column to the dataset, fill with =RAND()
sort the dataset on this column
copy the first 333 rows into a new sheet
copy the next 333 rows into a new sheet
I bet that would take less time than you've already spent trying to get the macros to work.
This revised macro will take the original 999 records and randomly distribute them into three other files (each file containing exactly 333 items) :
Sub croupier()
Dim k1 As Long, k2 As Long, k3 As Long
Dim Original As Workbook
Dim I As Long, ary(1 To 999)
Set Original = ActiveWorkbook
Dim rw As Long
Workbooks.Add
Set Winken = ActiveWorkbook
Workbooks.Add
Set Blinken = ActiveWorkbook
Workbooks.Add
Set Nod = ActiveWorkbook
k1 = 1
k2 = 1
k3 = 1
For I = 1 To 999
ary(I) = I
Next I
Call Shuffle(ary)
With Original.Sheets("Sheet1")
For I = 1 To 333
rw = ary(I)
.Cells(rw, 1).EntireRow.Copy Winken.Sheets("Sheet1").Cells(k1, 1)
k1 = k1 + 1
Next I
For I = 334 To 666
rw = ary(I)
.Cells(rw, 1).EntireRow.Copy Blinken.Sheets("Sheet1").Cells(k2, 1)
k2 = k2 + 1
Next I
For I = 667 To 999
rw = ary(I)
.Cells(rw, 1).EntireRow.Copy Nod.Sheets("Sheet1").Cells(k3, 1)
k3 = k3 + 1
Next I
End With
Winken.Save
Blinken.Save
Nod.Save
Winken.Close
Blinken.Close
Nod.Close
End Sub
Sub Shuffle(InOut() As Variant)
Dim HowMany As Long, I As Long, J As Long
Dim tempF As Double, temp As Variant
Hi = UBound(InOut)
Low = LBound(InOut)
ReDim Helper(Low To Hi) As Double
Randomize
For I = Low To Hi
Helper(I) = Rnd
Next I
J = (Hi - Low + 1) \ 2
Do While J > 0
For I = Low To Hi - J
If Helper(I) > Helper(I + J) Then
tempF = Helper(I)
Helper(I) = Helper(I + J)
Helper(I + J) = tempF
temp = InOut(I)
InOut(I) = InOut(I + J)
InOut(I + J) = temp
End If
Next I
For I = Hi - J To Low Step -1
If Helper(I) > Helper(I + J) Then
tempF = Helper(I)
Helper(I) = Helper(I + J)
Helper(I + J) = tempF
temp = InOut(I)
InOut(I) = InOut(I + J)
InOut(I + J) = temp
End If
Next I
J = J \ 2
Loop
End Sub
Here is a macro that will accept an array and copy to three different sheets:
Sub DoWork(Students As Variant)
Dim i As Long
Dim row As Integer
Dim sheetNumber As Integer
ReDim myArray(UBound(Students)) As Variant
Dim shuffledArray As Variant
Dim wkSheet As Worksheet
Dim myBooks(3) As Workbook
Set myBooks(1) = workBooks.Add
Set myBooks(2) = workBooks.Add
Set myBooks(3) = workBooks.Add
'populate the array with the number of rows
For i = 1 To UBound(Students)
myArray(i) = i
Next
'shuffle the array to provide true randomness
shuffledArray = ShuffleArray(myArray)
sheetNumber = 1
row = 1
'loop through the rows assiging to sheets
For i = 1 To UBound(Students)
If sheetNumber = 4 Then
sheetNumber = 1
row = row + 1
End If
Set wkSheet = myBooks(sheetNumber).ActiveSheet
wkSheet.Cells(row, 1) = Students(shuffledArray(i))
sheetNumber = sheetNumber + 1
Next
myBooks(1).SaveAs ("ws1.xlsx")
myBooks(2).SaveAs ("ws2.xlsx")
myBooks(3).SaveAs ("ws3.xlsx")
End Sub
Function ShuffleArray(InArray() As Variant) As Variant()
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' ShuffleArray
' This function returns the values of InArray in random order. The original
' InArray is not modified.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim N As Long
Dim Temp As Variant
Dim J As Long
Dim Arr() As Variant
Dim L As Long
Randomize
L = UBound(InArray) - LBound(InArray) + 1
ReDim Arr(LBound(InArray) To UBound(InArray))
For N = LBound(InArray) To UBound(InArray)
Arr(N) = InArray(N)
Next N
For N = LBound(Arr) To UBound(Arr)
J = CLng(((UBound(Arr) - N) * Rnd) + N)
Temp = Arr(N)
Arr(N) = Arr(J)
Arr(J) = Temp
Next N
ShuffleArray = Arr
End Function
Sub ShuffleArrayInPlace(InArray() As Variant)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' ShuffleArrayInPlace
' This shuffles InArray to random order, randomized in place.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim N As Long
Dim L As Long
Dim Temp As Variant
Dim J As Long
Randomize
L = UBound(InArray) - LBound(InArray) + 1
For N = LBound(InArray) To UBound(InArray)
J = CLng(((UBound(InArray) - N) * Rnd) + N)
If N <> J Then
Temp = InArray(N)
InArray(N) = InArray(J)
InArray(J) = Temp
End If
Next N
End Sub
You would then call with something like this:
Option Explicit
Option Base 1
Sub Test()
Dim i As Long
Dim Students(999) As Variant
'populate the array with the number of rows
For i = 1 To UBound(Students)
Students(i) = "Students-" & Str(i)
Next
DoWork (Students)
End Sub

Resources