VBA Count multiple duplicates in array - arrays

I have the same question as here: VBA counting multiple duplicates in array , but I haven't found an answer and with my reputation can't leave comment there.
I have an array with 150 numbers which could contain repetitive numbers from 1 to 50. Not always there are all 50 numbers in the array. Example of output of what I need:
- 10 times: 1, 2;
- 20 times: 3, 4 etc;
- 0 times: 5, 6, 7 etc.
I need to count how many combinations of duplicate numbers it has and what numbers are in those combinations including zero occurrence - which numbers are not in the array.
On mentioned above post there are solutions - but only when you know how many combinations of duplicates there are - and I don't know it - there could be 1 (all 150 numbers are equal) - ... - 20 ... up to 50 combinations if it contains all numbers from 1 to 50 three times each.
Appreciate any help and advice how to store output - finally it should be written to worksheet in the above mentioned format: [times] - [numbers] (here could be a string, example "5 - 6 - 7").
Here is what I've made for 5 combinations, but do 50 cases and then check 50 strings if they are empty or contain something to write to output is not very good option...
For i = 1 To totalNumbers 'my numbers from 1 to 50 or any other number
numberCount = 0
For j = 0 To UBound(friendsArray) 'my array of any size (in question said 150)
If i = friendsArray(j) Then
numberCount = numberCount + 1
End If
Next j
Select Case numberCount
Case 0
zeroString = zeroString & i & " - "
Case 1
oneString = oneString & i & " - "
Case 2
twoString = twoString & i & " - "
Case 3
threeString = threeString & i & " - "
Case 4
fourString = fourString & i & " - "
Case 5
fiveString = fiveString & i & " - "
Case Else
End Select
Next i

I have found possible option using Collection (but have got an headache with getting keys of collection...):
Dim col As New Collection
For i = 1 To totalNumbers
numberCount = 0
For j = 0 To UBound(friendsArray)
If i = friendsArray(j) Then
numberCount = numberCount + 1
End If
Next j
colValue = CStr(numberCount) & "> " & CStr(i) & " - " 'store current combination [key] and number as String
If IsMissing(col, CStr(numberCount)) Then
col.Add colValue, CStr(numberCount) 'if current combination of duplicates [key] is missing - add it to collection
Else 'if current combination [key] is already here - update the value [item]
oldValue = col(CStr(numberCount))
newValue = Replace(oldValue & colValue, CStr(numberCount) & "> ", "") 'delete combinations count
newValue = CStr(numberCount) & "> " & newValue
col.Remove CStr(numberCount) 'delete old value
col.Add newValue, CStr(numberCount) 'write new value with the same key
End If
Next i
For i = 1 To col.Count
Debug.Print col(i)
Next i
and IsMissing function (found here How to check the key is exists in collection or not)
Private Function IsMissing(col As Collection, field As String)
On Error GoTo IsMissingError
Dim val As Variant
val = col(field)
IsMissing = False
Exit Function
IsMissingError:
IsMissing = True
End Function
Output is like this [times]> [numbers]:
(array of 570 numbers)
114> 2 -
5> 6 -
17> 10 -
10> 3 - 8 - 19 - 21 - 30 -
6> 1 - 29 - 33 -
8> 5 - 9 - 13 - 23 - 25 - 28 - 37 - 40 -
4> 12 - 16 - 41 -
13> 43 -
12> 15 - 20 - 22 - 27 - 36 - 38 - 42 - 44 - 45 - 46 -
9> 4 - 7 - 11 - 14 - 34 - 47 - 48 -
7> 17 - 18 - 35 - 49 -
11> 24 - 26 - 31 - 32 - 39 - 50 -

Creating new array and count the number is more simple.
Sub test()
Dim friendsArray(0 To 50)
Dim vTable()
Dim iMax As Long
Dim a As Variant, b As Variant
Dim i As Long, s As Integer, n As Long
dim c As Integer
'Create Sample array to Test
n = UBound(friendsArray)
For i = 0 To n
friendsArray(i) = WorksheetFunction.RandBetween(0, 50)
Next i
'Your code
iMax = WorksheetFunction.Max(friendsArray)
ReDim vTable(0 To iMax) 'create new Array to count
For i = 0 To n
c = friendsArray(i)
vTable(c) = vTable(c) + 1
Next i
Dim dic As Object
Set dic = CreateObject("Scripting.Dictionary")
For i = 0 To iMax
If IsEmpty(vTable(i)) Then
s = 0
Else
s = vTable(i)
End If
If dic.Exists(s) Then
dic.Item(s) = dic.Item(s) & " - " & i
Else
dic.Add s, i
End If
Next i
a = dic.Keys
b = dic.Items
Range("a1").CurrentRegion.Clear
Range("B:B").NumberFormatLocal = "#"
Range("a1").Resize(UBound(a) + 1) = WorksheetFunction.Transpose(a)
Range("b1").Resize(UBound(b) + 1) = WorksheetFunction.Transpose(b)
Range("a1").CurrentRegion.Sort Range("a1"), xlAscending
End Sub

Related

How to assign an array's values to a range?

I'm trying to assign a 1-dimensional array's values to cell range.
For example; my array has 23 items (every item randomized from 1 to 5) and my cell range is range A1 to I7.
I want to assign every value of my array to this cell range randomly.
I randomize the cell values with my array but my array's values are not completely assigned to cells.
Sub define_ore_body()
Dim lb_grade As Integer, ub_grade As Integer
Dim ore_body(1 To 23) As Variant
Dim i As Integer, j As Integer, k As Integer
Dim a As Object
Dim b As Range
Application.ScreenUpdating = False
'my selected range area A1toI7
Set b = Application.Range("A1:I7")
Set a = Application.Cells
'******* low and high ore bound ******
lb_grade = InputBox("Enter lowest ore grade:")
ub_grade = InputBox("Enter highest ore grade:")
'The reason why I do it as follows is that if the random lower bound does not start from 1,
'the largest random number it generates is 2 more than the value I have entered, so
If lb_grade > 1 Then
ub_grade = ub_grade - 2
End If
'******* Random Array ******
'array has 23 items
For i = 1 To 23
ore_body(i) = Int((ub_grade * Rnd) + lb_grade)
Next i
'******* filling random cells with my array******
k = 1
For Each a In b
If a.Value = "" And k < 23 Then
b(Int(7 * Rnd + 1), (8 * Rnd + 1)) = ore_body(k)
ElseIf a.Count > 23 And k > 23 Then
Exit For
Else
k = k + 1
End If
Next a
'******* after filling cell now fill empty cells with Zero******
For i = 1 To 7
For j = 1 To 9
If Cells(i, j) = "" Then
Cells(i, j) = 0
Else
End If
Next j
Next i
'******* Coloring only containing array values******
For i = 1 To 7
For j = 1 To 9
If Cells(i, j) > 0 Then
Application.Cells(i, j).Interior.ColorIndex = 38
Else
End If
Next j
Next i
End Sub
The array contains 23 items that initialize to Variant/Empty:
Dim ore_body(1 To 23) As Variant
Make that 63 items that initialize to 0:
Dim ore_body(1 To 63) As Long
The rest of the code will now populate the first 23 elements, because that's what the loop does:
For i = 1 To 23
If you want the loop to run through all indexes, consider using LBound and UBound operators to programmatically retrieve the lower and upper boundaries of the array, respectively:
For i = LBound(ore_body) To UBound(ore_body)
Note that you have 23 hard-coded in several places, which is going to make it harder than necessary to modify if/when that 23 needs to be come a 25. Consider replacing every occurrence of it by a Const:
Const ElementCount As Long = 23
Then every instance of 23 can become ElementCount, and then when it needs to become 25 then there's only one place that needs any code to change.
Loop through the array.
Set a to a random cell in the range A1:I7.
If cell is empty put the value from the array in the cell, if it
isn't repeat step 2
Sub define_ore_body()
Dim lb_grade As Integer, ub_grade As Integer
Dim ore_body(1 To 23) As Variant
Dim i As Long, j As Long, k As Long
Dim a As Range
Dim b As Range
Application.ScreenUpdating = False
'my selected range area A1:I7
Set b = Application.Range("A1:I7")
' clear A1:A17
b.Clear
'******* low and high ore bound ******
lb_grade = InputBox("Enter lowest ore grade:")
ub_grade = InputBox("Enter highest ore grade:")
'The reason why I do it as follows is that if the random lower bound does not start from 1,
'the largest random number it generates is 2 more than the value I have entered, so
If lb_grade > 1 Then
ub_grade = ub_grade - 2
End If
'******* Random Array ******
'array has 23 items
For i = 1 To 23
ore_body(i) = Int((ub_grade * Rnd) + lb_grade)
Next i
'******* filling random cells with my array******
For k = 1 To 23
Do
Set a = b.Cells(Int(7 * Rnd) + 1, Int(9 * Rnd) + 1)
Loop Until a.Value = ""
a.Value = ore_body(k)
Next k
'******* after filling cell now fill empty cells with Zero******
For i = 1 To 7
For j = 1 To 9
If Cells(i, j) = "" Then
Cells(i, j) = 0
Else
End If
Next j
Next i
'******* Coloring only containing array values******
For i = 1 To 7
For j = 1 To 9
If Cells(i, j) > 0 Then
Application.Cells(i, j).Interior.ColorIndex = 38
Else
End If
Next j
Next i
End Sub

vba loop through array, store values to arrayi

I have some data, stored in arrays like
Dim arrA, arrB, arrC, arrAi, arrBi
Dim i as integer, x as integer
for i = 1 to 100
if cells(i,1).value = "criteria" then ' just add value to array when it meets some criteria
x = x + 1
arrA(x) = cells(i,1).value
arrB(x) = cells(i,2).value
arrC(x) = cells(i,3).value
end if
next i
redim preserve arrA(1 to x)
redim preserve arrB(1 to x)
redim preserve arrC(1 to x)
And the data looks like
arrA: 26.1 40.2 80.3 26.0 41.3 78.7 25.8 40.8 80.0
arrB: 10 11 10 66 67 64 32 32 33
arrC: 1 2 3 1 2 3 1 2 3
Since the values in arrA 26.1, 26.0, 25.8 (position 1, 4, 7) belong to group 1 (referencing to values in arrC at same position), I would like to store 26.1 26.0 25.8 to arrAi and 10 66 32 to arrBi for subsequent calculations.
How can I loop through the 3 arrays and store values to another array as described above?
Thanks in advance.
Try the next way, please:
Sub handleArraysFromArrays()
'your existing code...
'but you fistly must declare
Dim arrA(1 To 100), arrB(1 To 100), arrC(1 To 100)
'....
'your existing code
'...
Dim k As Long, kk As Long
ReDim arrAi(1 To UBound(arrA))
ReDim arrBi(1 To UBound(arrA))
For i = 1 To UBound(arrC)
If arrC(i, 1) = 1 Then k = k + 1: arrAi(k, 1) = arrA(i, 1)
If arrC(i, 1) = 2 Then kk = kk + 1: arrBi(kk, 1) = arrA(i, 1)
Next i
ReDim Preserve arrAi(1 To k): ReDim Preserve arrBi(1 To kk)
Debug.Print UBound(arrAi), UBound(arrBi)
End Sub

How create dynamic arrays to loop through a list and sum up values based on a criteria

Below I have a list of “ID” numbers with their associated “Number “and their “values”. I am trying to create sub function that crates a dynamic array that collects all the “Values” that have a “Number” that is equal to and lesser than 30. After the array is filled it is summed and placed under the heading titled “30 or less”. I have been trying do this using VBA with no luck. I have read a bunch of posts and documents telling me how to do this but I can’t make sense of it. Could someone show me how to get this done. Its driving me crazy and I am sure its simple I eventually want to expand this to do the same with “Numbers ” that are greater than 30 but less than 60 and so on. Thank you
ID Number Value
0 60 100
1 31 101
2 12 102
3 30 103
4 21 104
5 60 105
30 or less
Try pasting this into a new module in VB.
This presumes that your Raw data is in Sheet(1) and sorted data will be in a new blank worksheet Sheet(2)
Sub AddNumbers()
Dim RowNo, ColNo As Long
'Skip Header Row
RowNo = 2
Do Until Sheets(1).Cells(RowNo, 1) = ""
If Sheets(1).Cells(RowNo, 2) <= 30 Then
Sheets(2).Cells(1, 1) = "30 or less"
ColNo = 1
Sheets(2).Cells((Sheets(2).Cells(Rows.Count, 1).End(xlUp).row + 1), ColNo) = Sheets(1).Cells(RowNo, 3)
ElseIf Sheets(1).Cells(RowNo, 2) > 30 And Sheets(1).Cells(RowNo, 2) <= 60 Then
Sheets(2).Cells(1, 2) = "Between 30 and 60"
ColNo = 2
Sheets(2).Cells((Sheets(2).Cells(Rows.Count, 2).End(xlUp).row + 1), ColNo) = Sheets(1).Cells(RowNo, 3)
ElseIf Sheets(1).Cells(RowNo, 2) > 60 And Sheets(1).Cells(RowNo, 2) <= 90 Then
Sheets(2).Cells(1, 3) = "Between 60 and 90"
ColNo = 3
Sheets(2).Cells((Sheets(2).Cells(Rows.Count, 3).End(xlUp).row + 1), ColNo) = Sheets(1).Cells(RowNo, 3)
End If
RowNo = RowNo + 1
Loop
' Add Subtotals
ColNo = 1
Do Until Sheets(2).Cells(1, ColNo) = ""
Sheets(2).Cells((Sheets(2).Cells(Rows.Count, ColNo).End(xlUp).row + 1), ColNo).Formula = "=SUM(" & Col_Letter(ColNo) & "2:" & Col_Letter(ColNo) & (Sheets(2).Cells(Rows.Count, ColNo).End(xlUp).row) & ")"
Sheets(2).Cells((Sheets(2).Cells(Rows.Count, ColNo).End(xlUp).row), ColNo).Font.Bold = True
ColNo = ColNo + 1
Loop
End Sub
Function Col_Letter(lngCol As Long) As String
Dim vArr
vArr = Split(Cells(1, lngCol).Address(True, False), "$")
Col_Letter = vArr(0)
End Function

swapped rows vba code in excel

I'm trying to generate combinations id_users in excel that they have a relation..so I used this sample code to gave me this result:
the input is a pivot table :
' id_row id_users
10 1
2
3
66 4
11
this is my output
'source target label
1 2 10
1 3 10
2 3 10
4 11 66
But it gave me this result :
'source target label
1 2 10
1 3 10
2 1 10
2 3 10
3 1 10
3 2 10
4 11 66
11 4 66
as u see i didn't want to show the swapped rows too it's okay if it show duplicate row..but i don't want to show the swapped data..like : 1 2 and 2 1
it doesn't mean anything just a repeated info like : a with b has a relation and then u saw that b with a has a relation ..and it's just a repeated of an info that first i have it...
in my code is it's show the data array sq user_1 that not equal with the data in sqq user_2 , but also I want to show the data that are not called in pervious
sq user_1 :
'
'get the combinationsin the pivot table for this topic
sq = Range(rTopic, rTopic.End(xlDown).Offset(-1)).Offset(, 1).Resize(, 2).SpecialCells(2, 1).Value
'get the unique combinations of persons
sUniq = " "
For lUser_2 = 1 To UBound(sq, 1)
If InStr(sUniq, " " & sq(lUser_2, 2) & " ") = 0 Then
sUniq = sUniq & sq(lUser_2, 2) & " "
End If
Next
sqq = Split(Trim(sUniq))
'loop over user id's and generate combinations
For lUser_1 = 1 To UBound(sq, 1)
For lUser_2 = 0 To UBound(sqq)
If sq(lUser_1, 2) & "" <> sqq(lUser_2) Then
'we found a new combination, output to screen
Range(sStartingCellOutput).Offset(lRowOffset, lColOffset).Resize(1, 3).Value = Array(sq(lUser_1, 2), sqq(lUser_2), rTopic.Value)
'increment the counter
lRowOffset = lRowOffset + 1
If lRowOffset >= Rows.count Then
lRowOffset = 0
lColOffset = lColOffset + 4
End If
End If
Next
Next
I editted it to explain more in my code..just plz i need a samll help ? if my Q is not clear just comment for me...thanks
You need to add another space to the value you're checking
If InStr(sUniq, " " & sq(lUser_2, 2) & " ") = 0 Then

How to sum values in array from different columns + EXCEL VBA

I need to calculate this in excel vba ,using array loop only :
round 0 round 1
9 28
65 84
28 47
84 103
41 60
66 85
115 134
I need to sum values in round 0 in loop so the sum result (408) must be divided by 7 , if not I WANT to sum one value from the round 1 (in this case 84 instead of 65 ) to the rest of values in round 0 so the sum result can divided by 7 . There will be so many round up to 7 . I need VBA code to accomplish this..
Notes :
round 0 and round 1 all in one two-dimensional array
My Question is : is there a way to sum values from different columns in multi-dimensional array ??
there is an image attached .
I appreciate any help or idea .
Thanks in advance
Excel VBA Array Model:
http://im56.gulfup.com/8rDErI.png
Here an example file contains macro "Question1.xlsm"
http://www.gulfup.com/?TKAAYM
Notes : click the link under the big green down arrow to download the file.
UPDATE :
here another macro to the file "Question1.xlsm" :
Sub A1()
Dim arrTemp1() As Integer
Dim sum1 As Integer
arrblkTable1 = Sheets("Sheet1").Range("blkTable1").Value
ReDim Preserve arrTemp1(0 To 1, 1 To 7)
For a = 0 To 1
sum1 = 0
For c = 1 To 7
arrTemp1(a, c) = arrblkTable1(c, 1) + (a * 19)
text6 = text6 & arrTemp1(a, c) & vbCrLf
Worksheets("TEST3").Cells(a + 1, c).Value = arrTemp1(a, c)
sum1 = sum1 + arrTemp1(a, c)
Next c
If XLMod(sum1, 7) = 0 Then
MsgBox "Yes " & sum1
Else
MsgBox "No " & sum1
End If
Next a
MsgBox text6
End Sub
Function XLMod(a, b)
' This replicates the Excel MOD function
XLMod = a - b * Int(a / b)
End Function
UPDATE : here a new update to the previous macro :
Sub A1()
Dim arrTemp1(), arrTemp2(), arrSUMs() As Integer
Dim sum1 As Integer
arrblkTable1 = Sheets("Sheet1").Range("blkTable1").Value
arrblkTable2 = Sheets("Sheet1").Range("blkTable2").Value
'-------------------------------- arrTemp1 ------------------------------
ReDim Preserve arrTemp1(0 To 1, 1 To 7)
For a = 0 To 1
sum1 = 0
For c = 1 To 7
arrTemp1(a, c) = arrblkTable1(c, 1) + (a * 19)
text6 = text6 & arrTemp1(a, c) & vbCrLf
Worksheets("TEST3").Cells(a + 1, c).Value = arrTemp1(a, c)
sum1 = sum1 + arrTemp1(a, c)
Next c
If XLMod(sum1, 7) = 0 Then
MsgBox "Yes " & sum1
Else
MsgBox "No " & sum1
For c = 1 To 7
sum1 = sum1 - arrTemp1(a, c)
arrTemp1(a, c) = arrblkTable1(c, 1) + ((a + 1) * 19)
sum1 = sum1 + arrTemp1(a, c)
If XLMod(sum1, 7) = 0 Then
MsgBox "Yes " & sum1 & " " & arrTemp1(a, c)
End If
Next c
End If
Next a
For x = 0 To UBound(arrTemp1)
For y = 1 To UBound(arrTemp1)
text7 = text7 & arrTemp1(x, y) & vbCrLf
Next y
Next x
MsgBox text7
End Sub
Function XLMod(a, b)
' This replicates the Excel MOD function
XLMod = a - b * Int(a / b)
End Function
I need now to put each sum1 in one array , how I can do that ??
If I understood it correctly you want something similar to this:
Sub p()
v = Range("A2:A8")
v1 = Sheets("Sheet1").Range("B2:B8")
s = Application.WorksheetFunction.Sum(v)
b = False
Count = 0
For i = 1 To 7
temp = v
temp(i, 1) = v1(i, 1)
s = Application.WorksheetFunction.Sum(temp)
b = s Mod 7 = 0
If b = True Then
Count = Count + 1
End If
Next
MsgBox Count
End Sub
It may help tremendously if you can give more detail about what problem you're trying to solve, instead of focusing on how to solve it this way. There is a possibility that there's another way of doing it that hasn't occurred to you that will be much simpler.
This isn't an answer. Yet. But it will take more space than is allowed in a comment to ensure we've got this right.
For your sample data:
round 0 round 1
9 28
65 84
28 47
84 103
41 60
66 85
115 134
You want to:
Sum all the values in Round 0 (9 + 65 + 28 + 84 + 41 + 66 + 115) = 408
Take that sum (408) mod 7 and see if the result is 0
408 / 7 = 58.28, so (408 mod 7) <> 0
If the result isn't 0 (as in this case)
Start substituting numbers from round 1 for numbers in Round 0
Sum (28 + 65 + 28 + 84 + 41 + 66 + 115) = 427
427 / 7 = 61 (427 mod 7) = 0
This is now your valid result set.
Had the first number in Round 1 been 29
Sum (29 + 65 + 28 + 84 + 41 + 66 + 115) = 428
428 / 7 = 61.14 so (428 mod 7) <> 0
Substitute the next number from round 1 for the next number from round 0
Sum (9 + 84 + 28 + 84 + 41 + 66 + 115) = 427
This is now your valid result set.
Is that the logic you're after?
What happens if you get to the end of round 1 and you don't find a total that (mod 7 = 0)?

Resources